Skip to content

Commit c14ee77

Browse files
committed
Added various custom blocks:
- Stairs (StairsBlockWavefrontObject) - Doors (DoorWavefrontObject) - Fences (FenceWavefrontObject) - Redstone Dust (RedstoneWireWavefrontObject) - Slabs (SlabBlockWavefrontObject) - Iron Bars (IronBarsWavefrontObject) - Water (WaterWavefrontObject) - Lava (LavaWavefrontObject) - Fire (FireWavefrontObject) - Leaves (LeavesBlockWavefrontObject) - Cauldron (CauldronWavefrontObject) - Command Block (CommandBlockWavefrontObject) - Lit Furnace (LitFurnaceWavefrontObject) - Lit Pumpkin (LitPumpkinWavefrontObject) - Rough Prismarine Block (PrismarineRoughWavefrontObject) - Sea Lantern (SeaLanternWavefrontObject)
1 parent 77be363 commit c14ee77

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+5578
-617
lines changed

MCImportCleanUp_Blender.py

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
import bpy
2+
import bmesh
3+
4+
import re
5+
6+
from bpy import context
7+
8+
9+
#increase transparent_max_bounces if there are still black spot's visible on transparent parts
10+
for scene in bpy.data.scenes:
11+
scene.cycles.transparent_max_bounces = 32
12+
13+
POWER_VALUE = re.compile(r"^.+power_([0-9]+)")
14+
15+
MAX_EMISSION_VALUE = 2.0
16+
17+
ADD_BUMP_MAP = True
18+
19+
# Run through all materials of the current blend file
20+
for mat in bpy.data.materials:
21+
# If the material has a node tree
22+
if mat.node_tree:
23+
24+
# Check if material name has power in it
25+
isPowered = "power" in mat.name
26+
emission = 0.0
27+
28+
if isPowered:
29+
m = POWER_VALUE.match(mat.name)
30+
if m:
31+
val = int(m.group(1))
32+
if val > 0:
33+
emission = (val / 16) * MAX_EMISSION_VALUE
34+
35+
if "sea_lantern" in mat.name or "glowstone" in mat.name or "sea_lantern" in mat.name or "pumpkin_face_on" in mat.name or "lamp_on" in mat.name or "fire" in mat.name or "beacon" in mat.name:
36+
emission = (15 / 16) * MAX_EMISSION_VALUE
37+
38+
if "lava_" in mat.name:
39+
emission = (64 / 16) * MAX_EMISSION_VALUE
40+
41+
if "end_rod" in mat.name or "torch_on" in mat.name:
42+
emission = (14 / 16) * MAX_EMISSION_VALUE
43+
44+
if "furnace_front_on" in mat.name:
45+
emission = (13 / 16) * MAX_EMISSION_VALUE
46+
47+
if "lit_redstone_ore" in mat.name or "comparator_on" in mat.name or "repeater_on" in mat.name:
48+
emission = (9 / 16) * MAX_EMISSION_VALUE
49+
50+
if "torch_on" in mat.name:
51+
emission = (8 / 16) * MAX_EMISSION_VALUE
52+
53+
if "magma" in mat.name:
54+
emission = (4 / 16) * MAX_EMISSION_VALUE
55+
56+
if "mushroom_block_inside" in mat.name:
57+
emission = (1 / 16) * MAX_EMISSION_VALUE
58+
59+
isLit = "lit_" in mat.name
60+
61+
# Run through all nodes
62+
nodes = mat.node_tree.nodes
63+
links = mat.node_tree.links
64+
for node in nodes:
65+
if ADD_BUMP_MAP:
66+
#Check if node Bsdf Principled
67+
if type(node) is bpy.types.ShaderNodeBsdfPrincipled:
68+
#Get the NodeLink for the "Normal" socket
69+
normal_map_input = node.inputs["Normal"]
70+
#Check if the socket is connected
71+
if len(normal_map_input.links):
72+
#Get the first link for the socket
73+
normal_link = normal_map_input.links[0]
74+
#Check if the link is connected to a Normal Map
75+
if type(normal_link.from_node) is bpy.types.ShaderNodeNormalMap:
76+
#Get the Normal Map node
77+
normal_map_node = normal_link.from_node
78+
#Get the link for the Color socket
79+
normal_map_color_link = normal_map_node.inputs["Color"].links[0]
80+
81+
#Get the texture image node for the normal
82+
normal_texture_node = normal_map_color_link.from_node
83+
84+
#Create a new Bump node
85+
bump_node = nodes.new('ShaderNodeBump')
86+
87+
#Conntect the Alpha output of the texture image node to the Height input of the Bump node
88+
links.new(normal_texture_node.outputs['Alpha'], bump_node.inputs['Height'])
89+
90+
#Remove the link between the Bsdf Principled and Normal Map
91+
links.remove(normal_link)
92+
93+
#Connect the Normal Map output normal to the Bump Node "Normal" input socket
94+
links.new(normal_map_node.outputs['Normal'], bump_node.inputs['Normal'])
95+
96+
#Connect the Bump node to the Bsdf Principled Normal Socket
97+
links.new(bump_node.outputs['Normal'], node.inputs['Normal'])
98+
99+
if "water_" in mat.name:
100+
#Check if node Bsdf Principled
101+
if type(node) is bpy.types.ShaderNodeBsdfPrincipled:
102+
#Get the NodeLink for the "BSDF" output socket
103+
bsdf_output_node_link = node.outputs['BSDF']
104+
#Get the bsdf link
105+
bsdf_link = bsdf_output_node_link.links[0]
106+
#Get the material output node
107+
material_node = bsdf_link.to_node
108+
109+
#Get the NodeLink for the "Volume" input socket
110+
volume_input_node_link = material_node.inputs['Volume']
111+
#Check if the socket is not connected
112+
if len(volume_input_node_link.links) == 0:
113+
#Create principal volume node
114+
principal_volume_node = nodes.new('ShaderNodeVolumePrincipled')
115+
#Set It's color to black
116+
principal_volume_node.inputs['Color'].default_value = (0, 0, 0, 1)
117+
#Set the Density
118+
principal_volume_node.inputs['Density'].default_value = 0.11;
119+
120+
#Get the NodeLink for the "Base Color" input socket of the ShaderNodeBsdfPrincipled
121+
base_color_node_link = node.inputs['Base Color']
122+
#Get the base color link
123+
base_color_link = base_color_node_link.links[0]
124+
diffuse_texture_node = base_color_link.from_node
125+
126+
#Set the Absorption Color to the Image the diffuse texture
127+
links.new(diffuse_texture_node.outputs['Color'], principal_volume_node.inputs['Absorption Color'])
128+
129+
#Connect the material node volume socket to the principal volume node
130+
links.new(principal_volume_node.outputs['Volume'], material_node.inputs['Volume'])
131+
132+
133+
134+
if emission > 0.0:
135+
if type(node) is bpy.types.ShaderNodeBsdfPrincipled:
136+
node.inputs["Emission Strength"].default_value = emission
137+
elif isLit:
138+
if type(node) is bpy.types.ShaderNodeBsdfPrincipled:
139+
current = node.inputs["Emission Strength"].default_value
140+
if current > 0.0:
141+
node.inputs["Emission Strength"].default_value = (15/ 16) * MAX_EMISSION_VALUE
142+
143+
# If the node type is texture
144+
if node.type == 'TEX_IMAGE':
145+
# Set the interpolation -> Linear, Closest, Cubic, Smart
146+
node.interpolation = 'Closest'
147+
148+
#Now run through all node link
149+
for link in links:
150+
#Check if link is connected to a normal map node
151+
if type(link.to_node) is bpy.types.ShaderNodeNormalMap:
152+
if type(link.from_node) is bpy.types.ShaderNodeTexImage:
153+
imgNode = link.from_node
154+
imgNode.image.colorspace_settings.name = 'Non-Color'
155+
156+
157+
158+
159+
#Reset the normals in every selected object
160+
selection = bpy.context.selected_objects
161+
162+
#for o in selection:
163+
# bpy.context.view_layer.objects.active = o
164+
# try:
165+
# bpy.ops.mesh.customdata_custom_splitnormals_clear()
166+
# except Exception as ex:
167+
# print(ex)

README.MD

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,31 @@ Usage:`schem2obj -i <input schematic> (-t <resource pack path>) -o <output obj>
88
Optional parameters:<br>
99
- `-t` The absolute or relative path to the resource pack (folder with `pack.mcdata`)
1010
- `-allBlocks` If set, all blocks (including hidden) are exported
11+
- `-snowy` If set, only the snowy grass is generated for now
1112

12-
All basic blocks (that are only basic cubes) are supported. The ones that do use only a basic cube and are not working properly or rely on parent blocks to work properly (ex, fences, stairs...) are currently being implemented.
13+
To import the generated OBJ into Blender 3.4 for example, create a new collection and import the obj into the newly created collection.
14+
Once It's imported, to properly set up the import, use the companion `MCImportCleanUp_Blender.py` script
15+
16+
All basic blocks (that are only basic cubes), and some built blocks (ex, water and lava) are supported. Other built in blocks (ex, bed, player head...) or blocks that don't work are currently being implemented.
1317
<br>List of custom blocks implemented:
1418
- Grass Block (non-snowy and snowy)
15-
- Magma Block (fixed compressed texture)
1619
- Glass Pane & Block (regular and stained)
20+
- Stairs
21+
- Doors
22+
- Fences
23+
- Redstone Dust
24+
- Slabs
25+
- Iron Bars
26+
- Water
27+
- Lava
28+
- Fire
29+
- Leaves
30+
- Cauldron
31+
- Command Block
32+
- Lit Furnace
33+
- Lit Pumpkin
34+
- Rough Prismarine Block
35+
- Sea Lantern
36+
- Magma Block (fixed compressed texture)
1737

1838
Currently in development.

src/main/java/com/davixdevelop/schem2obj/Constants.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,30 @@
77
import com.davixdevelop.schem2obj.wavefront.WavefrontCollection;
88
import com.davixdevelop.schem2obj.wavefront.material.MaterialCollection;
99

10+
import java.util.HashMap;
11+
import java.util.Map;
12+
1013
public class Constants {
1114

15+
public static void setConstants(){
16+
REDSTONE_COLORS.put(0, 4915200);
17+
REDSTONE_COLORS.put(1,7274496);
18+
REDSTONE_COLORS.put(2,7929856);
19+
REDSTONE_COLORS.put(3,8519680);
20+
REDSTONE_COLORS.put(4,9175040);
21+
REDSTONE_COLORS.put(5,9895936);
22+
REDSTONE_COLORS.put(6,10551296);
23+
REDSTONE_COLORS.put(7,11206656);
24+
REDSTONE_COLORS.put(8,11862016);
25+
REDSTONE_COLORS.put(9,12517376);
26+
REDSTONE_COLORS.put(10,13238272);
27+
REDSTONE_COLORS.put(11,13828096);
28+
REDSTONE_COLORS.put(12,14483456);
29+
REDSTONE_COLORS.put(13,15140352);
30+
REDSTONE_COLORS.put(14,15801088);
31+
REDSTONE_COLORS.put(15,16527616);
32+
}
33+
1234
public static final SchematicHolder LOADED_SCHEMATIC = new SchematicHolder();
1335

1436
public static final BlockModelCollection BLOCK_MODELS = new BlockModelCollection();
@@ -24,5 +46,11 @@ public class Constants {
2446

2547
public static int SNOW_COLOR = 16316922;
2648

49+
public static Map<Integer, Integer> REDSTONE_COLORS = new HashMap<>();
50+
51+
public static boolean IS_SNOWY = false;
52+
2753
public static final Double[] BLOCK_ORIGIN = new Double[] {0.5,0.5,0.5};
54+
55+
2856
}

src/main/java/com/davixdevelop/schem2obj/SchemeToObj.java

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
import com.davixdevelop.schem2obj.namespace.Namespace;
44
import com.davixdevelop.schem2obj.schematic.Schematic;
5-
import com.davixdevelop.schem2obj.utilities.Utility;
5+
import com.davixdevelop.schem2obj.util.Utility;
66
import com.davixdevelop.schem2obj.wavefront.*;
7+
import com.davixdevelop.schem2obj.wavefront.custom.LavaWavefrontObject;
8+
import com.davixdevelop.schem2obj.wavefront.custom.WaterWavefrontObject;
79
import com.davixdevelop.schem2obj.wavefront.material.IMaterial;
810
import com.davixdevelop.schem2obj.wavefront.material.json.PackTemplate;
911
import com.google.gson.Gson;
@@ -24,6 +26,8 @@ public static void main(String[] arg) {
2426

2527
String rootFolder = Paths.get(".").toAbsolutePath().normalize().toString();
2628

29+
Constants.setConstants();
30+
2731
if(arg.length >= 4) {
2832
//Get scheme file from arguments
2933
if(arg[0].startsWith("-i")){
@@ -112,6 +116,8 @@ public static void main(String[] arg) {
112116
while (nextArgIndex < arg.length){
113117
if(arg[nextArgIndex].equals("-allBlocks"))
114118
exportAllBlock = true;
119+
else if(arg[nextArgIndex].equals("-snowy"))
120+
Constants.IS_SNOWY = true;
115121
nextArgIndex += 1;
116122
}
117123
}
@@ -122,7 +128,7 @@ public static void main(String[] arg) {
122128

123129
ArrayList<IWavefrontObject> objects = s.schemToObj(schem_path, exportAllBlock);
124130

125-
if(objects.isEmpty()){
131+
if(objects == null || objects.isEmpty()){
126132
Utility.Log("Failed to convert schematic to OBJ");
127133
return;
128134
}
@@ -170,6 +176,9 @@ public ArrayList<IWavefrontObject> schemToObj(String schemPath, boolean exportAl
170176
Integer length = (int) Constants.LOADED_SCHEMATIC.getLength();
171177
Integer height = (int) Constants.LOADED_SCHEMATIC.getHeight();
172178

179+
WaterWavefrontObject waterObject = null;
180+
LavaWavefrontObject lavaObject = null;
181+
173182
for (int x = 0; x < width; x++) {
174183
for (int y = 0; y < height; y++) {
175184
for(int z = 0; z < length; z++) {
@@ -180,14 +189,31 @@ public ArrayList<IWavefrontObject> schemToObj(String schemPath, boolean exportAl
180189

181190
Namespace blockNamespace = Constants.LOADED_SCHEMATIC.getNamespace(x, y, z);
182191

183-
//ToDo: Write custom blocks (ex, Water, Chest, Sign, Wall Sign...). Until then, ignore these blocks
184-
if (blockNamespace == null || blockNamespace.getDomain().equals("builtin"))
192+
//ToDo: Write custom blocks (ex, Chest, Sign, Wall Sign...). Until then, ignore these blocks
193+
if (blockNamespace == null || blockNamespace.getDomain().equals("builtin")){
194+
if(blockNamespace != null) {
195+
if (blockNamespace.getType().equals("flowing_water") || blockNamespace.getType().equals("water")) {
196+
if (waterObject == null)
197+
waterObject = new WaterWavefrontObject();
198+
199+
waterObject.addBlock(blockNamespace);
200+
}
201+
202+
if (blockNamespace.getType().equals("flowing_lava") || blockNamespace.getType().equals("lava")) {
203+
if(lavaObject == null)
204+
lavaObject = new LavaWavefrontObject();
205+
206+
lavaObject.addBlock(blockNamespace);
207+
}
208+
}
209+
185210
if (exportAllBlocks)
186211
continue;
187212
else {
188213
allBlocks.put(index, null);
189214
continue;
190215
}
216+
}
191217

192218
//Get singleton wavefrontBlock from memory
193219
IWavefrontObject wavefrontObject = Constants.WAVEFRONT_COLLECTION.fromNamespace(blockNamespace);
@@ -205,6 +231,8 @@ public ArrayList<IWavefrontObject> schemToObj(String schemPath, boolean exportAl
205231
}
206232

207233
if(!exportAllBlocks){
234+
HashMap<Double[], Double[]> allNormals = new HashMap<>();
235+
208236
//If exportAllBlocks all blocks is false, loop through allBlocks from bottom to top and delete hidden faces
209237
for (int y = 0; y < height; y++){
210238
for (int x = 0; x < width; x++){
@@ -257,11 +285,38 @@ public ArrayList<IWavefrontObject> schemToObj(String schemPath, boolean exportAl
257285
if(WavefrontUtility.checkFacing(objectBoundingFaces, object, allBlocks.get(x + ((y - 1) * length + z) * width), "down", "up"))
258286
object.deleteFaces("down");
259287

288+
if(!object.getMaterialFaces().isEmpty()) {
289+
//Reset the normals for the object to reflect the deleted faces and add them to all normals
290+
WavefrontUtility.resetNormals(allNormals, object);
291+
}
292+
260293
blocks.add(object);
261294
}
262295
}
263296
}
264297
}
298+
/*
299+
//Normalize allNormals
300+
WavefrontUtility.normalizeNormals(allNormals);
301+
302+
//Copy allNormals back to each object
303+
for(int c = 0; c < blocks.size(); c++){
304+
IWavefrontObject object = blocks.get(c);
305+
WavefrontUtility.copyAllNormalsToObject(allNormals, object);
306+
blocks.set(c, object);
307+
}*/
308+
309+
310+
}
311+
312+
if(waterObject != null){
313+
waterObject.finalizeObject();
314+
blocks.add(waterObject);
315+
}
316+
317+
if(lavaObject != null){
318+
lavaObject.finalizeObject();
319+
blocks.add(lavaObject);
265320
}
266321

267322

0 commit comments

Comments
 (0)