update
i suck at git. to whoever is reading this in the future, please excuse this absolutely terrible commit message and know that i'm doing my best. cheers & love. once dawd³ is in a better shape these commit messages will get better. for now they're terrible because the sheer amount of experimentation makes me forget that i have to `git commit` my work every so often.
This commit is contained in:
parent
367f2fc80e
commit
337c9d08e1
129 changed files with 2072 additions and 244 deletions
|
|
@ -55,6 +55,8 @@ loom {
|
||||||
runDir("build/datagen")
|
runDir("build/datagen")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
accessWidenerPath.set(file("src/main/resources/dawd3.accesswidener"))
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceSets.named(mainSourceSet.name) {
|
sourceSets.named(mainSourceSet.name) {
|
||||||
|
|
|
||||||
BIN
proj/block/fader_bottom.ase
Normal file
BIN
proj/block/fader_bottom.ase
Normal file
Binary file not shown.
BIN
proj/block/fader_handle.ase
Normal file
BIN
proj/block/fader_handle.ase
Normal file
Binary file not shown.
BIN
proj/block/fader_side.ase
Normal file
BIN
proj/block/fader_side.ase
Normal file
Binary file not shown.
BIN
proj/block/fader_top.ase
Normal file
BIN
proj/block/fader_top.ase
Normal file
Binary file not shown.
BIN
proj/block/sine_oscillator_front.ase
Normal file
BIN
proj/block/sine_oscillator_front.ase
Normal file
Binary file not shown.
BIN
proj/block/sine_oscillator_side.ase
Normal file
BIN
proj/block/sine_oscillator_side.ase
Normal file
Binary file not shown.
BIN
proj/block/template/device_side.ase
Normal file
BIN
proj/block/template/device_side.ase
Normal file
Binary file not shown.
BIN
proj/cable_color_palette.ase
Normal file
BIN
proj/cable_color_palette.ase
Normal file
Binary file not shown.
BIN
proj/device/cable.ase
Normal file
BIN
proj/device/cable.ase
Normal file
Binary file not shown.
Binary file not shown.
BIN
proj/item/black_patch_cable.ase
Normal file
BIN
proj/item/black_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/blue_patch_cable.ase
Normal file
BIN
proj/item/blue_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/brown_patch_cable.ase
Normal file
BIN
proj/item/brown_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/cyan_patch_cable.ase
Normal file
BIN
proj/item/cyan_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/gray_patch_cable.ase
Normal file
BIN
proj/item/gray_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/green_patch_cable.ase
Normal file
BIN
proj/item/green_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/light_blue_patch_cable.ase
Normal file
BIN
proj/item/light_blue_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/light_gray_patch_cable.ase
Normal file
BIN
proj/item/light_gray_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/lime_patch_cable.ase
Normal file
BIN
proj/item/lime_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/magenta_patch_cable.ase
Normal file
BIN
proj/item/magenta_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/orange_patch_cable.ase
Normal file
BIN
proj/item/orange_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/pink_patch_cable.ase
Normal file
BIN
proj/item/pink_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/purple_patch_cable.ase
Normal file
BIN
proj/item/purple_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/red_patch_cable.ase
Normal file
BIN
proj/item/red_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/white_patch_cable.ase
Normal file
BIN
proj/item/white_patch_cable.ase
Normal file
Binary file not shown.
BIN
proj/item/yellow_patch_cable.ase
Normal file
BIN
proj/item/yellow_patch_cable.ase
Normal file
Binary file not shown.
|
|
@ -1,4 +1,26 @@
|
||||||
// 1.19.2 2022-11-20T23:04:16.520652716 Models
|
// 1.19.2 2022-11-27T14:23:10.976127707 Models
|
||||||
e3c6aacd49a6395f37047d3df31f91a18a411267 assets/dawd3/models/item/speaker.json
|
0812a674d14cfc6fbb7c0e2ac1b473bf2afe1965 assets/dawd3/models/item/brown_patch_cable.json
|
||||||
|
bf0e322e33123cb6873c2da4e8c6ab85688deb4e assets/dawd3/models/item/gray_patch_cable.json
|
||||||
|
6a9fb209d82556f5941422a8d047a0ae2af1dc8f assets/dawd3/models/item/sine_oscillator.json
|
||||||
|
215b221d48639e96de11914625a770a389d65b81 assets/dawd3/models/item/green_patch_cable.json
|
||||||
|
e34001d3c974aecfa347d435beb6bc0b9d325897 assets/dawd3/models/item/cyan_patch_cable.json
|
||||||
|
8ba890b28c5ac57c59f19ccc8c72825caac10677 assets/dawd3/models/item/magenta_patch_cable.json
|
||||||
f7b47538f17992177e97e06842c0039ae5096b2b assets/dawd3/blockstates/speaker.json
|
f7b47538f17992177e97e06842c0039ae5096b2b assets/dawd3/blockstates/speaker.json
|
||||||
bd0adfc8b3dc271042dd4b19a8cace4e0fffedfe assets/dawd3/models/block/speaker.json
|
9cf2cff42345ec60a944d7399b5047323aa8e88c assets/dawd3/models/item/red_patch_cable.json
|
||||||
|
8c6f0307320980a66c70622b0b7c72c8cfe78dc3 assets/dawd3/models/item/light_gray_patch_cable.json
|
||||||
|
5ed33e9ec3c70e8cc027eea6425951d51487437f assets/dawd3/models/item/blue_patch_cable.json
|
||||||
|
83866bdfd40b759257070558c9ccb942e082914a assets/dawd3/blockstates/fader.json
|
||||||
|
a4e8bc89d39021eb8d56ad7735216cb851d67287 assets/dawd3/models/item/light_blue_patch_cable.json
|
||||||
|
0023521ecb90e79515f0fa0088609c70aac9a605 assets/dawd3/models/block/speaker.json
|
||||||
|
8aa966337109315240614d5257eb72f959eba5d8 assets/dawd3/models/item/orange_patch_cable.json
|
||||||
|
12c4bfd825b2476955afcd3bb23c1f736ce68caa assets/dawd3/models/item/yellow_patch_cable.json
|
||||||
|
aab1bce7ec4e7c7dccd3d33d4242de12b63a981d assets/dawd3/models/item/white_patch_cable.json
|
||||||
|
6cdda36e539e23acf3db70d761338e88932a6ebb assets/dawd3/models/item/purple_patch_cable.json
|
||||||
|
e3c6aacd49a6395f37047d3df31f91a18a411267 assets/dawd3/models/item/speaker.json
|
||||||
|
32bb0e6e3bf75b9005602e8fb1042ad5d41286ad assets/dawd3/models/item/lime_patch_cable.json
|
||||||
|
956d8f117df95cf62c8cac375cff853df96840d6 assets/dawd3/models/item/pink_patch_cable.json
|
||||||
|
71fd99012b4338600f0a11dcadb9a97a6e0a15e5 assets/dawd3/models/block/fader.json
|
||||||
|
3b1811bab3ba394ba03b67ac2efc72cb35316dc8 assets/dawd3/blockstates/sine_oscillator.json
|
||||||
|
9c18a8292a0c9990cd23bebf5c6191c2114ccc6d assets/dawd3/models/item/black_patch_cable.json
|
||||||
|
d300f52d5aa2dcb2ec7d9a0cdb1138104e016d83 assets/dawd3/models/item/fader.json
|
||||||
|
0d759623578e206f437f2d9976443a773721a9f3 assets/dawd3/models/block/sine_oscillator.json
|
||||||
|
|
|
||||||
19
src/main/generated/assets/dawd3/blockstates/fader.json
Normal file
19
src/main/generated/assets/dawd3/blockstates/fader.json
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"facing=east": {
|
||||||
|
"model": "dawd3:block/fader",
|
||||||
|
"y": 90
|
||||||
|
},
|
||||||
|
"facing=north": {
|
||||||
|
"model": "dawd3:block/fader"
|
||||||
|
},
|
||||||
|
"facing=south": {
|
||||||
|
"model": "dawd3:block/fader",
|
||||||
|
"y": 180
|
||||||
|
},
|
||||||
|
"facing=west": {
|
||||||
|
"model": "dawd3:block/fader",
|
||||||
|
"y": 270
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"facing=east": {
|
||||||
|
"model": "dawd3:block/sine_oscillator",
|
||||||
|
"y": 90
|
||||||
|
},
|
||||||
|
"facing=north": {
|
||||||
|
"model": "dawd3:block/sine_oscillator"
|
||||||
|
},
|
||||||
|
"facing=south": {
|
||||||
|
"model": "dawd3:block/sine_oscillator",
|
||||||
|
"y": 180
|
||||||
|
},
|
||||||
|
"facing=west": {
|
||||||
|
"model": "dawd3:block/sine_oscillator",
|
||||||
|
"y": 270
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
src/main/generated/assets/dawd3/models/block/fader.json
Normal file
77
src/main/generated/assets/dawd3/models/block/fader.json
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"parent": "block/block",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"faces": {
|
||||||
|
"down": {
|
||||||
|
"cullface": "down",
|
||||||
|
"texture": "#bottom"
|
||||||
|
},
|
||||||
|
"east": {
|
||||||
|
"cullface": "east",
|
||||||
|
"texture": "#right"
|
||||||
|
},
|
||||||
|
"north": {
|
||||||
|
"cullface": "north",
|
||||||
|
"texture": "#front"
|
||||||
|
},
|
||||||
|
"south": {
|
||||||
|
"cullface": "south",
|
||||||
|
"texture": "#back"
|
||||||
|
},
|
||||||
|
"up": {
|
||||||
|
"cullface": "up",
|
||||||
|
"texture": "#top"
|
||||||
|
},
|
||||||
|
"west": {
|
||||||
|
"cullface": "west",
|
||||||
|
"texture": "#left"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"from": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
16.0,
|
||||||
|
8.0,
|
||||||
|
16.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"faces": {
|
||||||
|
"north": {
|
||||||
|
"cullface": "north",
|
||||||
|
"texture": "#port",
|
||||||
|
"uv": [
|
||||||
|
4.0,
|
||||||
|
0.0,
|
||||||
|
8.0,
|
||||||
|
4.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"from": [
|
||||||
|
6.0,
|
||||||
|
3.0,
|
||||||
|
-0.01
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
10.0,
|
||||||
|
7.0,
|
||||||
|
0.01
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"textures": {
|
||||||
|
"back": "dawd3:block/fader_side",
|
||||||
|
"bottom": "dawd3:block/fader_bottom",
|
||||||
|
"front": "dawd3:block/fader_side",
|
||||||
|
"left": "dawd3:block/fader_side",
|
||||||
|
"particle": "dawd3:block/fader_side",
|
||||||
|
"port": "dawd3:device/port",
|
||||||
|
"right": "dawd3:block/fader_side",
|
||||||
|
"top": "dawd3:block/fader_top"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
{
|
||||||
|
"parent": "block/block",
|
||||||
|
"elements": [
|
||||||
|
{
|
||||||
|
"faces": {
|
||||||
|
"down": {
|
||||||
|
"cullface": "down",
|
||||||
|
"texture": "#bottom"
|
||||||
|
},
|
||||||
|
"east": {
|
||||||
|
"cullface": "east",
|
||||||
|
"texture": "#right"
|
||||||
|
},
|
||||||
|
"north": {
|
||||||
|
"cullface": "north",
|
||||||
|
"texture": "#front"
|
||||||
|
},
|
||||||
|
"south": {
|
||||||
|
"cullface": "south",
|
||||||
|
"texture": "#back"
|
||||||
|
},
|
||||||
|
"up": {
|
||||||
|
"cullface": "up",
|
||||||
|
"texture": "#top"
|
||||||
|
},
|
||||||
|
"west": {
|
||||||
|
"cullface": "west",
|
||||||
|
"texture": "#left"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"from": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
0.0
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
16.0,
|
||||||
|
16.0,
|
||||||
|
16.0
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"faces": {
|
||||||
|
"north": {
|
||||||
|
"cullface": "north",
|
||||||
|
"texture": "#port",
|
||||||
|
"uv": [
|
||||||
|
0.0,
|
||||||
|
0.0,
|
||||||
|
4.0,
|
||||||
|
4.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"from": [
|
||||||
|
9.0,
|
||||||
|
6.0,
|
||||||
|
-0.01
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
13.0,
|
||||||
|
10.0,
|
||||||
|
0.01
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"faces": {
|
||||||
|
"north": {
|
||||||
|
"cullface": "north",
|
||||||
|
"texture": "#port",
|
||||||
|
"uv": [
|
||||||
|
4.0,
|
||||||
|
0.0,
|
||||||
|
8.0,
|
||||||
|
4.0
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"from": [
|
||||||
|
3.0,
|
||||||
|
6.0,
|
||||||
|
-0.01
|
||||||
|
],
|
||||||
|
"to": [
|
||||||
|
7.0,
|
||||||
|
10.0,
|
||||||
|
0.01
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"textures": {
|
||||||
|
"back": "dawd3:block/sine_oscillator_side",
|
||||||
|
"bottom": "dawd3:block/sine_oscillator_side",
|
||||||
|
"front": "dawd3:block/sine_oscillator_front",
|
||||||
|
"left": "dawd3:block/sine_oscillator_side",
|
||||||
|
"particle": "dawd3:block/sine_oscillator_side",
|
||||||
|
"port": "dawd3:device/port",
|
||||||
|
"right": "dawd3:block/sine_oscillator_side",
|
||||||
|
"top": "dawd3:block/sine_oscillator_side"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -5,11 +5,11 @@
|
||||||
"faces": {
|
"faces": {
|
||||||
"down": {
|
"down": {
|
||||||
"cullface": "down",
|
"cullface": "down",
|
||||||
"texture": "#side"
|
"texture": "#bottom"
|
||||||
},
|
},
|
||||||
"east": {
|
"east": {
|
||||||
"cullface": "east",
|
"cullface": "east",
|
||||||
"texture": "#side"
|
"texture": "#right"
|
||||||
},
|
},
|
||||||
"north": {
|
"north": {
|
||||||
"cullface": "north",
|
"cullface": "north",
|
||||||
|
|
@ -17,15 +17,15 @@
|
||||||
},
|
},
|
||||||
"south": {
|
"south": {
|
||||||
"cullface": "south",
|
"cullface": "south",
|
||||||
"texture": "#side"
|
"texture": "#back"
|
||||||
},
|
},
|
||||||
"up": {
|
"up": {
|
||||||
"cullface": "up",
|
"cullface": "up",
|
||||||
"texture": "#side"
|
"texture": "#top"
|
||||||
},
|
},
|
||||||
"west": {
|
"west": {
|
||||||
"cullface": "west",
|
"cullface": "west",
|
||||||
"texture": "#side"
|
"texture": "#left"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"from": [
|
"from": [
|
||||||
|
|
@ -53,21 +53,25 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"from": [
|
"from": [
|
||||||
6.5,
|
6.0,
|
||||||
6.5,
|
3.0,
|
||||||
15.99
|
15.99
|
||||||
],
|
],
|
||||||
"to": [
|
"to": [
|
||||||
9.5,
|
10.0,
|
||||||
9.5,
|
7.0,
|
||||||
16.01
|
16.01
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"textures": {
|
"textures": {
|
||||||
|
"back": "dawd3:block/speaker_side",
|
||||||
|
"bottom": "dawd3:block/speaker_side",
|
||||||
"front": "dawd3:block/speaker_front",
|
"front": "dawd3:block/speaker_front",
|
||||||
"particle": "#side",
|
"left": "dawd3:block/speaker_side",
|
||||||
|
"particle": "dawd3:block/speaker_side",
|
||||||
"port": "dawd3:device/port",
|
"port": "dawd3:device/port",
|
||||||
"side": "dawd3:block/speaker_side"
|
"right": "dawd3:block/speaker_side",
|
||||||
|
"top": "dawd3:block/speaker_side"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/black_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/blue_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/brown_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/cyan_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/main/generated/assets/dawd3/models/item/fader.json
Normal file
3
src/main/generated/assets/dawd3/models/item/fader.json
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"parent": "dawd3:block/fader"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/gray_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/green_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/light_blue_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/light_gray_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/lime_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/magenta_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/orange_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/pink_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/purple_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/red_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"parent": "dawd3:block/sine_oscillator"
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/white_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"parent": "minecraft:item/generated",
|
||||||
|
"textures": {
|
||||||
|
"layer0": "dawd3:item/yellow_patch_cable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -6,8 +6,9 @@ import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents
|
||||||
import net.liquidev.d3r.D3r
|
import net.liquidev.d3r.D3r
|
||||||
import net.liquidev.dawd3.audio.Audio
|
import net.liquidev.dawd3.audio.Audio
|
||||||
import net.liquidev.dawd3.block.Blocks
|
import net.liquidev.dawd3.block.Blocks
|
||||||
import net.liquidev.dawd3.block.entity.registerBlockEntityEvents
|
import net.liquidev.dawd3.block.entity.registerClientBlockEntityEvents
|
||||||
import net.liquidev.dawd3.item.Items
|
import net.liquidev.dawd3.item.Items
|
||||||
|
import net.liquidev.dawd3.net.Packets
|
||||||
import org.slf4j.Logger
|
import org.slf4j.Logger
|
||||||
import org.slf4j.LoggerFactory
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
|
@ -34,7 +35,9 @@ object Mod : ModInitializer, ClientModInitializer {
|
||||||
D3r.unload()
|
D3r.unload()
|
||||||
}
|
}
|
||||||
|
|
||||||
registerBlockEntityEvents()
|
registerClientBlockEntityEvents()
|
||||||
|
Blocks.initializeClient()
|
||||||
|
Packets.registerClientReceivers()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun loggerName(name: String?): String =
|
private fun loggerName(name: String?): String =
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,12 @@ package net.liquidev.dawd3.audio
|
||||||
|
|
||||||
import net.liquidev.d3r.D3r
|
import net.liquidev.d3r.D3r
|
||||||
import net.liquidev.dawd3.Mod
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.audio.generator.GeneratorWithProcessingState
|
||||||
import net.liquidev.dawd3.audio.generator.MixGenerator
|
import net.liquidev.dawd3.audio.generator.MixGenerator
|
||||||
import net.liquidev.dawd3.audio.unit.Decibels
|
|
||||||
|
|
||||||
/** Audio system and common settings. */
|
/** Audio system and common settings. */
|
||||||
object Audio {
|
object Audio {
|
||||||
val logger = Mod.logger<Audio>()
|
private val logger = Mod.logger<Audio>()
|
||||||
|
|
||||||
const val sampleRate = 48000
|
const val sampleRate = 48000
|
||||||
const val sampleRateF = sampleRate.toFloat()
|
const val sampleRateF = sampleRate.toFloat()
|
||||||
|
|
@ -18,13 +18,15 @@ object Audio {
|
||||||
private val outputStreamId: Int
|
private val outputStreamId: Int
|
||||||
|
|
||||||
val mixer = MixGenerator()
|
val mixer = MixGenerator()
|
||||||
|
private val processingStateAdapter = GeneratorWithProcessingState(mixer)
|
||||||
|
val processingState get() = processingStateAdapter.processingState
|
||||||
|
|
||||||
init {
|
init {
|
||||||
logger.info("initializing")
|
logger.info("initializing")
|
||||||
logger.info("${Decibels(-3.0f).toAmplitude()}")
|
|
||||||
D3r.openDefaultHost()
|
D3r.openDefaultHost()
|
||||||
outputDeviceId = D3r.openDefaultOutputDevice()
|
outputDeviceId = D3r.openDefaultOutputDevice()
|
||||||
outputStreamId = D3r.openOutputStream(outputDeviceId, sampleRate, 1, bufferSize, mixer)
|
outputStreamId =
|
||||||
|
D3r.openOutputStream(outputDeviceId, sampleRate, 1, bufferSize, processingStateAdapter)
|
||||||
D3r.startPlayback(outputStreamId)
|
D3r.startPlayback(outputStreamId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,6 @@ package net.liquidev.dawd3.audio.device
|
||||||
interface Device {
|
interface Device {
|
||||||
fun process(sampleCount: Int, channels: Int)
|
fun process(sampleCount: Int, channels: Int)
|
||||||
|
|
||||||
fun visitInputPorts(visit: (PortName, InputPort) -> Unit) {}
|
fun visitInputPorts(visit: (InputPortName, InputPort) -> Unit) {}
|
||||||
fun visitOutputPorts(visit: (PortName, OutputPort) -> Unit) {}
|
fun visitOutputPorts(visit: (OutputPortName, OutputPort) -> Unit) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package net.liquidev.dawd3.audio.device
|
||||||
|
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
|
||||||
|
interface DeviceDescriptor {
|
||||||
|
val id: Identifier
|
||||||
|
}
|
||||||
|
|
@ -23,8 +23,15 @@ class DeviceInstance(val state: Device) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun process(sampleCount: Int, channels: Int) {
|
fun process(sampleCount: Int, channels: Int, processingState: ProcessingState) {
|
||||||
state.process(sampleCount, channels)
|
if (this !in processingState.processedDevices) {
|
||||||
|
println("processing $this")
|
||||||
|
processingState.processedDevices.add(this)
|
||||||
|
for ((_, port) in inputPortsByName) {
|
||||||
|
port.connectedOutput?.owningDevice?.process(sampleCount, channels, processingState)
|
||||||
|
}
|
||||||
|
state.process(sampleCount, channels)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,45 @@
|
||||||
package net.liquidev.dawd3.audio.device
|
package net.liquidev.dawd3.audio.device
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
|
||||||
/** Device utility functions. */
|
/** Device utility functions. */
|
||||||
object Devices {
|
object Devices {
|
||||||
|
private val logger = Mod.logger<Devices>()
|
||||||
|
|
||||||
|
data class InputAndOutputDevicePortPairs<T>(
|
||||||
|
val outputDevice: T,
|
||||||
|
val outputPort: OutputPortName,
|
||||||
|
val inputDevice: T,
|
||||||
|
val inputPort: InputPortName,
|
||||||
|
)
|
||||||
|
|
||||||
|
fun <T> sortPortsByInputAndOutput(
|
||||||
|
fromData: T,
|
||||||
|
fromPort: PortName,
|
||||||
|
toData: T,
|
||||||
|
toPort: PortName,
|
||||||
|
): InputAndOutputDevicePortPairs<T>? =
|
||||||
|
when {
|
||||||
|
fromPort is OutputPortName && toPort is InputPortName -> InputAndOutputDevicePortPairs(
|
||||||
|
outputDevice = fromData,
|
||||||
|
outputPort = fromPort,
|
||||||
|
inputDevice = toData,
|
||||||
|
inputPort = toPort,
|
||||||
|
)
|
||||||
|
fromPort is InputPortName && toPort is OutputPortName -> InputAndOutputDevicePortPairs(
|
||||||
|
outputDevice = toData,
|
||||||
|
outputPort = toPort,
|
||||||
|
inputDevice = fromData,
|
||||||
|
inputPort = fromPort,
|
||||||
|
)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
fun makeConnection(
|
fun makeConnection(
|
||||||
from: DeviceInstance,
|
from: DeviceInstance,
|
||||||
outputPortName: PortName,
|
outputPortName: PortName,
|
||||||
to: DeviceInstance,
|
to: DeviceInstance,
|
||||||
inputPortName: PortName
|
inputPortName: PortName,
|
||||||
) {
|
) {
|
||||||
val outputPort = from.outputPortsByName[outputPortName]
|
val outputPort = from.outputPortsByName[outputPortName]
|
||||||
val inputPort = to.inputPortsByName[inputPortName]
|
val inputPort = to.inputPortsByName[inputPortName]
|
||||||
|
|
@ -20,34 +53,58 @@ object Devices {
|
||||||
|
|
||||||
inputPort.connectedOutput = outputPort
|
inputPort.connectedOutput = outputPort
|
||||||
outputPort.connectedInputs.add(inputPort)
|
outputPort.connectedInputs.add(inputPort)
|
||||||
|
logger.debug("made connection between ports $outputPortName ($outputPort) and $inputPortName ($inputPort)")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun severAllConnectionsInInputPort(inputPort: InputPort): Int {
|
||||||
|
var total = 0
|
||||||
|
val connectedOutput = inputPort.connectedOutput
|
||||||
|
if (connectedOutput != null) {
|
||||||
|
connectedOutput.connectedInputs.remove(inputPort)
|
||||||
|
inputPort.connectedOutput = null
|
||||||
|
total += 1
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun severAllConnectionsInOutputPort(outputPort: OutputPort): Int {
|
||||||
|
var total = 0
|
||||||
|
for (port in outputPort.connectedInputs) {
|
||||||
|
port.connectedOutput = null
|
||||||
|
}
|
||||||
|
total += outputPort.connectedInputs.size
|
||||||
|
outputPort.connectedInputs.clear()
|
||||||
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Disconnects everything from the given port. Returns the number of ports disconnected. */
|
/** Disconnects everything from the given port. Returns the number of ports disconnected. */
|
||||||
fun severAllConnections(device: DeviceInstance, portName: PortName): Int {
|
fun severAllConnectionsInPort(device: DeviceInstance, portName: PortName): Int {
|
||||||
var total = 0
|
var total = 0
|
||||||
|
|
||||||
val inputPort = device.inputPortsByName[portName]
|
val inputPort = device.inputPortsByName[portName]
|
||||||
if (inputPort != null) {
|
if (inputPort != null) {
|
||||||
val connectedOutput = inputPort.connectedOutput
|
total += severAllConnectionsInInputPort(inputPort)
|
||||||
if (connectedOutput != null) {
|
|
||||||
connectedOutput.connectedInputs.remove(inputPort)
|
|
||||||
inputPort.connectedOutput = null
|
|
||||||
total += 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val outputPort = device.outputPortsByName[portName]
|
val outputPort = device.outputPortsByName[portName]
|
||||||
if (outputPort != null) {
|
if (outputPort != null) {
|
||||||
for (port in outputPort.connectedInputs) {
|
total += severAllConnectionsInOutputPort(outputPort)
|
||||||
port.connectedOutput = null
|
|
||||||
}
|
|
||||||
total += outputPort.connectedInputs.size
|
|
||||||
outputPort.connectedInputs.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return total
|
return total
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun severAllConnectionsInDevice(device: DeviceInstance): Int {
|
||||||
|
var total = 0
|
||||||
|
for ((_, inputPort) in device.inputPortsByName) {
|
||||||
|
total += severAllConnectionsInInputPort(inputPort)
|
||||||
|
}
|
||||||
|
for ((_, outputPort) in device.outputPortsByName) {
|
||||||
|
total += severAllConnectionsInOutputPort(outputPort)
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
||||||
|
|
||||||
private fun noSuchPort(device: DeviceInstance, name: PortName): NoSuchPortException =
|
private fun noSuchPort(device: DeviceInstance, name: PortName): NoSuchPortException =
|
||||||
NoSuchPortException("device $device does not have port with name $name")
|
NoSuchPortException("device $device does not have port with name $name")
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package net.liquidev.dawd3.audio.device
|
||||||
|
|
||||||
|
class ProcessingState {
|
||||||
|
val processedDevices = mutableSetOf<DeviceInstance>()
|
||||||
|
|
||||||
|
fun reset() {
|
||||||
|
processedDevices.clear()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,45 +0,0 @@
|
||||||
package net.liquidev.dawd3.audio.device
|
|
||||||
|
|
||||||
import net.liquidev.dawd3.Mod
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sim is the simulator for device graphs.
|
|
||||||
*/
|
|
||||||
class Sim {
|
|
||||||
private companion object {
|
|
||||||
val logger = Mod.logger<Sim>()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val stack = arrayListOf<DeviceInstance>()
|
|
||||||
private val evalList = arrayListOf<DeviceInstance>()
|
|
||||||
private val visited = arrayListOf<DeviceInstance>()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Builds a simulation sequence: establishes the order in which devices in a graph have to be
|
|
||||||
* processed so that we arrive at the input signals fed into the terminal.
|
|
||||||
*/
|
|
||||||
fun rebuild(terminal: DeviceInstance) {
|
|
||||||
stack.add(terminal)
|
|
||||||
|
|
||||||
evalList.clear()
|
|
||||||
while (stack.isNotEmpty()) {
|
|
||||||
val device = stack.removeAt(stack.size - 1)
|
|
||||||
evalList.add(device)
|
|
||||||
for ((_, inputPort) in device.inputPortsByName) {
|
|
||||||
val connectedOutputPort = inputPort.connectedOutput
|
|
||||||
if (connectedOutputPort != null) {
|
|
||||||
stack.add(connectedOutputPort.owningDevice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info("rebuilt device graph; new evaluation order (reversed): $evalList")
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Process all devices in the built sequence. */
|
|
||||||
fun simulate(sampleCount: Int, channels: Int) {
|
|
||||||
for (device in evalList.asReversed()) {
|
|
||||||
device.process(sampleCount, channels)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,14 +1,18 @@
|
||||||
package net.liquidev.dawd3.audio.device
|
package net.liquidev.dawd3.audio.device
|
||||||
|
|
||||||
import net.liquidev.dawd3.audio.AudioBuffer
|
import net.liquidev.dawd3.audio.AudioBuffer
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
|
||||||
class InputPort {
|
sealed class Port {
|
||||||
|
lateinit var owningDevice: DeviceInstance
|
||||||
|
}
|
||||||
|
|
||||||
|
class InputPort : Port() {
|
||||||
private companion object {
|
private companion object {
|
||||||
val emptyBuffer = AudioBuffer()
|
val emptyBuffer = AudioBuffer()
|
||||||
}
|
}
|
||||||
|
|
||||||
var connectedOutput: OutputPort? = null
|
var connectedOutput: OutputPort? = null
|
||||||
lateinit var owningDevice: DeviceInstance
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convenience function that returns an empty buffer if there is no connected port, or the
|
* Convenience function that returns an empty buffer if there is no connected port, or the
|
||||||
|
|
@ -24,15 +28,76 @@ class InputPort {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class OutputPort(bufferCount: Int) {
|
class OutputPort(bufferCount: Int) : Port() {
|
||||||
init {
|
init {
|
||||||
require(bufferCount >= 1) { "output port must have at least one buffer" }
|
require(bufferCount >= 1) { "output port must have at least one buffer" }
|
||||||
}
|
}
|
||||||
|
|
||||||
val buffers = Array(bufferCount) { AudioBuffer() }
|
val buffers = Array(bufferCount) { AudioBuffer() }
|
||||||
val connectedInputs = hashSetOf<InputPort>()
|
val connectedInputs = hashSetOf<InputPort>()
|
||||||
lateinit var owningDevice: DeviceInstance
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Marker class that port name markers should inherit from. */
|
enum class PortDirection {
|
||||||
abstract class PortName
|
Input,
|
||||||
|
Output,
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Marker interface that port name markers should inherit from. */
|
||||||
|
sealed interface PortName {
|
||||||
|
val id: Identifier
|
||||||
|
val direction: PortDirection
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val registry = hashMapOf<Identifier, PortName>()
|
||||||
|
|
||||||
|
internal fun register(name: PortName) {
|
||||||
|
assert(name.id !in registry) { "there must not be two ports with the same name" }
|
||||||
|
registry[name.id] = name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun fromString(name: String): PortName? {
|
||||||
|
return registry[Identifier(name)]
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun idInDevice(deviceId: Identifier, name: String): Identifier =
|
||||||
|
Identifier(deviceId.namespace, "${deviceId.path}/$name")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InputPortName(override val id: Identifier) : PortName {
|
||||||
|
override val direction = PortDirection.Input
|
||||||
|
|
||||||
|
init {
|
||||||
|
PortName.register(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
parent: Identifier,
|
||||||
|
name: String,
|
||||||
|
) : this(PortName.idInDevice(parent, name))
|
||||||
|
|
||||||
|
override fun toString(): String = id.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
class OutputPortName private constructor(
|
||||||
|
override val id: Identifier,
|
||||||
|
private val instanceOf: OutputPortName?,
|
||||||
|
) : PortName {
|
||||||
|
override val direction = PortDirection.Output
|
||||||
|
|
||||||
|
init {
|
||||||
|
PortName.register(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
parent: Identifier,
|
||||||
|
name: String,
|
||||||
|
) : this(PortName.idInDevice(parent, name), instanceOf = null)
|
||||||
|
|
||||||
|
override fun toString(): String = id.toString()
|
||||||
|
|
||||||
|
fun makeInstanced(instanceName: String) =
|
||||||
|
OutputPortName(PortName.idInDevice(id, instanceName), instanceOf = this)
|
||||||
|
|
||||||
|
fun resolveInstance() = instanceOf ?: this
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
package net.liquidev.dawd3.audio.devices
|
package net.liquidev.dawd3.audio.devices
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
import net.liquidev.dawd3.audio.device.Device
|
import net.liquidev.dawd3.audio.device.Device
|
||||||
|
import net.liquidev.dawd3.audio.device.DeviceDescriptor
|
||||||
import net.liquidev.dawd3.audio.device.OutputPort
|
import net.liquidev.dawd3.audio.device.OutputPort
|
||||||
import net.liquidev.dawd3.audio.device.PortName
|
import net.liquidev.dawd3.audio.device.OutputPortName
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
|
||||||
class ConstantDevice(var value: Float) : Device {
|
class ConstantDevice(var value: Float) : Device {
|
||||||
object Output : PortName()
|
companion object : DeviceDescriptor {
|
||||||
|
override val id = Identifier(Mod.id, "constant")
|
||||||
|
val outputPort = OutputPortName(id, "output")
|
||||||
|
}
|
||||||
|
|
||||||
val output = OutputPort(bufferCount = 1)
|
val output = OutputPort(bufferCount = 1)
|
||||||
|
|
||||||
|
|
@ -16,7 +22,7 @@ class ConstantDevice(var value: Float) : Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitOutputPorts(visit: (PortName, OutputPort) -> Unit) {
|
override fun visitOutputPorts(visit: (OutputPortName, OutputPort) -> Unit) {
|
||||||
visit(Output, output)
|
visit(outputPort, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,20 +1,22 @@
|
||||||
package net.liquidev.dawd3.audio.devices
|
package net.liquidev.dawd3.audio.devices
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
import net.liquidev.dawd3.audio.Audio
|
import net.liquidev.dawd3.audio.Audio
|
||||||
import net.liquidev.dawd3.audio.device.Device
|
import net.liquidev.dawd3.audio.device.*
|
||||||
import net.liquidev.dawd3.audio.device.InputPort
|
import net.minecraft.util.Identifier
|
||||||
import net.liquidev.dawd3.audio.device.OutputPort
|
|
||||||
import net.liquidev.dawd3.audio.device.PortName
|
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
private const val twoPi = 2.0f * kotlin.math.PI.toFloat()
|
private const val twoPi = 2.0f * kotlin.math.PI.toFloat()
|
||||||
|
|
||||||
class SineOscillatorDevice : Device {
|
class SineOscillatorDevice : Device {
|
||||||
object FrequencyCV : PortName()
|
companion object : DeviceDescriptor {
|
||||||
object Output : PortName()
|
override val id = Identifier(Mod.id, "sine_oscillator")
|
||||||
|
val frequencyCVPort = InputPortName(id, "frequency_cv")
|
||||||
|
val outputPort = OutputPortName(id, "output")
|
||||||
|
}
|
||||||
|
|
||||||
val frequencyCV = InputPort()
|
private val frequencyCV = InputPort()
|
||||||
val output = OutputPort(bufferCount = 1)
|
private val output = OutputPort(bufferCount = 1)
|
||||||
|
|
||||||
private var phase = 0.0f
|
private var phase = 0.0f
|
||||||
|
|
||||||
|
|
@ -29,11 +31,11 @@ class SineOscillatorDevice : Device {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitInputPorts(visit: (PortName, InputPort) -> Unit) {
|
override fun visitInputPorts(visit: (InputPortName, InputPort) -> Unit) {
|
||||||
visit(FrequencyCV, frequencyCV)
|
visit(frequencyCVPort, frequencyCV)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitOutputPorts(visit: (PortName, OutputPort) -> Unit) {
|
override fun visitOutputPorts(visit: (OutputPortName, OutputPort) -> Unit) {
|
||||||
visit(Output, output)
|
visit(outputPort, output)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,11 +1,17 @@
|
||||||
package net.liquidev.dawd3.audio.devices
|
package net.liquidev.dawd3.audio.devices
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
import net.liquidev.dawd3.audio.device.Device
|
import net.liquidev.dawd3.audio.device.Device
|
||||||
|
import net.liquidev.dawd3.audio.device.DeviceDescriptor
|
||||||
import net.liquidev.dawd3.audio.device.InputPort
|
import net.liquidev.dawd3.audio.device.InputPort
|
||||||
import net.liquidev.dawd3.audio.device.PortName
|
import net.liquidev.dawd3.audio.device.InputPortName
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
|
||||||
class TerminalDevice : Device {
|
class TerminalDevice : Device {
|
||||||
object Input : PortName()
|
companion object : DeviceDescriptor {
|
||||||
|
override val id = Identifier(Mod.id, "terminal")
|
||||||
|
val inputPort = InputPortName(id, "input")
|
||||||
|
}
|
||||||
|
|
||||||
val input = InputPort()
|
val input = InputPort()
|
||||||
|
|
||||||
|
|
@ -15,7 +21,7 @@ class TerminalDevice : Device {
|
||||||
// speakers.
|
// speakers.
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitInputPorts(visit: (PortName, InputPort) -> Unit) {
|
override fun visitInputPorts(visit: (InputPortName, InputPort) -> Unit) {
|
||||||
visit(Input, input)
|
visit(inputPort, input)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,25 +1,19 @@
|
||||||
package net.liquidev.dawd3.audio.generator
|
package net.liquidev.dawd3.audio.generator
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.audio.Audio
|
||||||
import net.liquidev.dawd3.audio.device.DeviceInstance
|
import net.liquidev.dawd3.audio.device.DeviceInstance
|
||||||
import net.liquidev.dawd3.audio.device.Sim
|
|
||||||
import net.liquidev.dawd3.audio.devices.TerminalDevice
|
import net.liquidev.dawd3.audio.devices.TerminalDevice
|
||||||
|
|
||||||
/** Audio generator that evaluates a device graph. */
|
/** Audio generator that evaluates a device graph. */
|
||||||
class DeviceGraphGenerator : AudioGenerator() {
|
class DeviceGraphGenerator : AudioGenerator() {
|
||||||
private val sim = Sim()
|
|
||||||
private val terminalDeviceState = TerminalDevice()
|
private val terminalDeviceState = TerminalDevice()
|
||||||
val terminalDevice = DeviceInstance(terminalDeviceState)
|
val terminalDevice = DeviceInstance(terminalDeviceState)
|
||||||
|
|
||||||
/**
|
|
||||||
* This should be called whenever the device graph changes to reestablish what order devices
|
|
||||||
* should be processed in.
|
|
||||||
*/
|
|
||||||
fun rebuildSim() {
|
|
||||||
sim.rebuild(terminalDevice)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun generate(output: FloatArray, sampleCount: Int, channelCount: Int) {
|
override fun generate(output: FloatArray, sampleCount: Int, channelCount: Int) {
|
||||||
sim.simulate(sampleCount, channelCount)
|
// TODO: Maybe passing in the static processingState here is not the cleanest way to go
|
||||||
|
// about things, but I don't see how we could inject that context into this function
|
||||||
|
// without jumping through significant hoops.
|
||||||
|
terminalDevice.process(sampleCount, channelCount, Audio.processingState)
|
||||||
val buffer = terminalDeviceState.input.getConnectedOutputBuffer(0, sampleCount)
|
val buffer = terminalDeviceState.input.getConnectedOutputBuffer(0, sampleCount)
|
||||||
for (i in 0 until sampleCount) {
|
for (i in 0 until sampleCount) {
|
||||||
output[i] = buffer[i]
|
output[i] = buffer[i]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
package net.liquidev.dawd3.audio.generator
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.audio.device.ProcessingState
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapter for adding device ProcessingState into an existing generator.
|
||||||
|
* The processing state is reset with every call to generate().
|
||||||
|
*/
|
||||||
|
class GeneratorWithProcessingState(val inner: AudioGenerator) : AudioGenerator() {
|
||||||
|
val processingState = ProcessingState()
|
||||||
|
|
||||||
|
override fun generate(output: FloatArray, sampleCount: Int, channelCount: Int) {
|
||||||
|
inner.generate(output, sampleCount, channelCount)
|
||||||
|
processingState.reset()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,19 +11,19 @@ class MixGenerator : AudioGenerator() {
|
||||||
val logger = Mod.logger<MixGenerator>()
|
val logger = Mod.logger<MixGenerator>()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val taskQueue = TaskQueue()
|
private val taskQueue = TaskQueue<Unit, Unit>()
|
||||||
private val channels = arrayListOf<WeakReference<Channel<AudioGenerator>>>()
|
private val channels = arrayListOf<WeakReference<Channel<AudioGenerator>>>()
|
||||||
|
|
||||||
fun <T : AudioGenerator> createChannel(generator: T): Channel<T> {
|
fun <T : AudioGenerator> createChannel(generator: T): Channel<T> {
|
||||||
val channel = Channel(taskQueue, generator)
|
val channel = Channel(taskQueue, generator)
|
||||||
val weak = WeakReference(channel as Channel<AudioGenerator>)
|
val weak = WeakReference(channel as Channel<AudioGenerator>)
|
||||||
taskQueue.execute { channels.add(weak) }
|
taskQueue.enqueue { channels.add(weak) }
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun generate(output: FloatArray, sampleCount: Int, channelCount: Int) {
|
override fun generate(output: FloatArray, sampleCount: Int, channelCount: Int) {
|
||||||
// Flush task queue as soon as possible to reduce latency.
|
// Flush task queue as soon as possible to reduce latency.
|
||||||
taskQueue.flush()
|
taskQueue.flush(Unit)
|
||||||
reapStoppedChannels()
|
reapStoppedChannels()
|
||||||
|
|
||||||
for (i in 0 until sampleCount) {
|
for (i in 0 until sampleCount) {
|
||||||
|
|
@ -61,7 +61,7 @@ class MixGenerator : AudioGenerator() {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Channel<out T : AudioGenerator>(
|
class Channel<out T : AudioGenerator>(
|
||||||
private val taskQueue: TaskQueue,
|
private val taskQueue: TaskQueue<Unit, Unit>,
|
||||||
val generator: T,
|
val generator: T,
|
||||||
) {
|
) {
|
||||||
private companion object {
|
private companion object {
|
||||||
|
|
@ -75,7 +75,7 @@ class MixGenerator : AudioGenerator() {
|
||||||
|
|
||||||
/** Shuts down the channel on the next audio generation request. */
|
/** Shuts down the channel on the next audio generation request. */
|
||||||
fun stop() {
|
fun stop() {
|
||||||
taskQueue.execute { playing = false }
|
taskQueue.enqueue { playing = false }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun finalize() {
|
fun finalize() {
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
package net.liquidev.dawd3.audio.generator
|
package net.liquidev.dawd3.audio.generator
|
||||||
|
|
||||||
import net.liquidev.dawd3.audio.Audio
|
import net.liquidev.dawd3.audio.Audio
|
||||||
import net.liquidev.dawd3.audio.unit.Amplitude
|
|
||||||
import kotlin.math.sin
|
import kotlin.math.sin
|
||||||
|
|
||||||
class SineOscGenerator(frequency: Float, private val amplitude: Amplitude) : AudioGenerator() {
|
class SineOscGenerator(frequency: Float) : AudioGenerator() {
|
||||||
private val phaseStep = (1.0f / Audio.sampleRate.toFloat()) * frequency
|
private val phaseStep = (1.0f / Audio.sampleRate.toFloat()) * frequency
|
||||||
private var phase = 0.0f
|
private var phase = 0.0f
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
package net.liquidev.dawd3.block
|
package net.liquidev.dawd3.block
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry
|
||||||
import net.fabricmc.fabric.api.item.v1.FabricItemSettings
|
import net.fabricmc.fabric.api.item.v1.FabricItemSettings
|
||||||
|
import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings
|
||||||
import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder
|
import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder
|
||||||
import net.liquidev.dawd3.D3Registry
|
import net.liquidev.dawd3.D3Registry
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
import net.liquidev.dawd3.block.device.AnyDeviceBlockDescriptor
|
import net.liquidev.dawd3.block.device.AnyDeviceBlockDescriptor
|
||||||
import net.liquidev.dawd3.block.device.DeviceBlock
|
import net.liquidev.dawd3.block.device.DeviceBlock
|
||||||
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||||
|
import net.liquidev.dawd3.block.device.DeviceBlockEntityRenderer
|
||||||
|
import net.liquidev.dawd3.block.devices.FaderBlockDescriptor
|
||||||
|
import net.liquidev.dawd3.block.devices.SineOscillatorBlockDescriptor
|
||||||
import net.liquidev.dawd3.block.devices.SpeakerBlockDescriptor
|
import net.liquidev.dawd3.block.devices.SpeakerBlockDescriptor
|
||||||
import net.liquidev.dawd3.item.Items
|
import net.liquidev.dawd3.item.Items
|
||||||
import net.minecraft.block.Block
|
import net.minecraft.block.Block
|
||||||
|
import net.minecraft.block.Material
|
||||||
import net.minecraft.block.entity.BlockEntityType
|
import net.minecraft.block.entity.BlockEntityType
|
||||||
import net.minecraft.item.BlockItem
|
import net.minecraft.item.BlockItem
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
|
|
@ -43,8 +50,27 @@ object Blocks {
|
||||||
return registeredDeviceBlock
|
return registeredDeviceBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// To not have to mess with rendering the vertices ourselves we just use a baked model that's
|
||||||
|
// represented as a block.
|
||||||
|
val patchCablePlug = Registry.register(
|
||||||
|
Registry.BLOCK,
|
||||||
|
Identifier(Mod.id, "patch_cable_plug"),
|
||||||
|
Block(FabricBlockSettings.of(Material.METAL))
|
||||||
|
)
|
||||||
|
|
||||||
// Device blocks
|
// Device blocks
|
||||||
val speaker = registerDeviceBlock(SpeakerBlockDescriptor)
|
val speaker = registerDeviceBlock(SpeakerBlockDescriptor)
|
||||||
|
val sineOscillator = registerDeviceBlock(SineOscillatorBlockDescriptor)
|
||||||
|
val fader = registerDeviceBlock(FaderBlockDescriptor)
|
||||||
|
|
||||||
fun initialize() {}
|
fun initialize() {}
|
||||||
|
|
||||||
|
fun initializeClient() {
|
||||||
|
for ((_, deviceBlock) in deviceBlocks) {
|
||||||
|
BlockEntityRendererRegistry.register(
|
||||||
|
deviceBlock.blockEntity,
|
||||||
|
::DeviceBlockEntityRenderer
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,27 +1,70 @@
|
||||||
package net.liquidev.dawd3.block.device
|
package net.liquidev.dawd3.block.device
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.common.*
|
||||||
|
import net.liquidev.dawd3.item.PatchCableItem
|
||||||
import net.minecraft.block.*
|
import net.minecraft.block.*
|
||||||
import net.minecraft.block.entity.BlockEntity
|
import net.minecraft.block.entity.BlockEntity
|
||||||
import net.minecraft.item.ItemPlacementContext
|
import net.minecraft.item.ItemPlacementContext
|
||||||
import net.minecraft.state.StateManager
|
import net.minecraft.state.StateManager
|
||||||
import net.minecraft.state.property.Properties
|
import net.minecraft.state.property.Properties
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.util.math.Vec3f
|
||||||
|
import net.minecraft.util.shape.VoxelShape
|
||||||
|
import net.minecraft.util.shape.VoxelShapes
|
||||||
|
import net.minecraft.world.BlockView
|
||||||
|
import net.minecraft.world.World
|
||||||
|
|
||||||
class DeviceBlock(private val descriptor: AnyDeviceBlockDescriptor) :
|
class DeviceBlock(private val descriptor: AnyDeviceBlockDescriptor) :
|
||||||
BlockWithEntity(descriptor.blockSettings),
|
BlockWithEntity(descriptor.blockSettings),
|
||||||
BlockEntityProvider {
|
BlockEntityProvider {
|
||||||
|
|
||||||
|
val outlineCuboids = HorizontalDirection.values().map { direction ->
|
||||||
|
val cuboid = descriptor.cuboid
|
||||||
|
val centerOrigin = Vec3f(0.5f, 0.5f, 0.5f)
|
||||||
|
val from = cuboid.fromF - centerOrigin
|
||||||
|
val to = cuboid.toF - centerOrigin
|
||||||
|
Cuboids.newF(direction.rotateY(from) + centerOrigin, direction.rotateY(to) + centerOrigin)
|
||||||
|
}
|
||||||
|
|
||||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||||
builder.add(Properties.HORIZONTAL_FACING)
|
builder.add(Properties.HORIZONTAL_FACING)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getPlacementState(context: ItemPlacementContext): BlockState =
|
override fun getPlacementState(context: ItemPlacementContext): BlockState =
|
||||||
defaultState.with(Properties.HORIZONTAL_FACING, context.playerFacing.opposite)
|
defaultState.with(
|
||||||
|
Properties.HORIZONTAL_FACING,
|
||||||
|
context.playerFacing.opposite
|
||||||
|
)
|
||||||
|
|
||||||
@Deprecated("do not call directly")
|
@Deprecated("do not call this function directly")
|
||||||
override fun getRenderType(state: BlockState): BlockRenderType =
|
override fun getRenderType(state: BlockState): BlockRenderType =
|
||||||
BlockRenderType.MODEL
|
BlockRenderType.MODEL
|
||||||
|
|
||||||
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity =
|
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity =
|
||||||
DeviceBlockEntity.factory(descriptor).create(pos, state)
|
DeviceBlockEntity.factory(descriptor).create(pos, state)
|
||||||
|
|
||||||
|
@Deprecated("do not call this function directly")
|
||||||
|
override fun onStateReplaced(
|
||||||
|
state: BlockState,
|
||||||
|
world: World,
|
||||||
|
position: BlockPos,
|
||||||
|
newState: BlockState,
|
||||||
|
moved: Boolean,
|
||||||
|
) {
|
||||||
|
PatchCableItem.onBlockDestroyed(position)
|
||||||
|
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
super.onStateReplaced(state, world, position, newState, moved)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated("do not call this function directly")
|
||||||
|
override fun getOutlineShape(
|
||||||
|
state: BlockState,
|
||||||
|
world: BlockView,
|
||||||
|
pos: BlockPos,
|
||||||
|
context: ShapeContext,
|
||||||
|
): VoxelShape {
|
||||||
|
val direction = HorizontalDirection.fromDirection(state[Properties.HORIZONTAL_FACING])!!
|
||||||
|
return VoxelShapes.cuboid(outlineCuboids[direction.index])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,16 +1,20 @@
|
||||||
package net.liquidev.dawd3.block.device
|
package net.liquidev.dawd3.block.device
|
||||||
|
|
||||||
|
import FaceTextures
|
||||||
import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings
|
import net.fabricmc.fabric.api.`object`.builder.v1.block.FabricBlockSettings
|
||||||
import net.liquidev.dawd3.audio.device.DeviceInstance
|
import net.liquidev.dawd3.audio.device.DeviceInstance
|
||||||
|
import net.liquidev.dawd3.common.Cuboids
|
||||||
import net.minecraft.block.AbstractBlock
|
import net.minecraft.block.AbstractBlock
|
||||||
import net.minecraft.block.Material
|
import net.minecraft.block.Material
|
||||||
import net.minecraft.client.world.ClientWorld
|
import net.minecraft.client.world.ClientWorld
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.math.Box
|
||||||
|
|
||||||
typealias AnyDeviceBlockDescriptor = DeviceBlockDescriptor<DeviceBlockDescriptor.ClientState, Any>
|
typealias AnyDeviceBlockDescriptor = DeviceBlockDescriptor<DeviceBlockDescriptor.ClientState, Any>
|
||||||
|
|
||||||
interface DeviceBlockDescriptor<out CS : DeviceBlockDescriptor.ClientState, out ServerState> {
|
interface DeviceBlockDescriptor<out CS : DeviceBlockDescriptor.ClientState, out ServerState> {
|
||||||
val id: Identifier
|
val id: Identifier
|
||||||
|
|
||||||
val blockSettings: AbstractBlock.Settings
|
val blockSettings: AbstractBlock.Settings
|
||||||
// The default block settings are used for metal-enclosed modules.
|
// The default block settings are used for metal-enclosed modules.
|
||||||
get() = FabricBlockSettings
|
get() = FabricBlockSettings
|
||||||
|
|
@ -18,7 +22,12 @@ interface DeviceBlockDescriptor<out CS : DeviceBlockDescriptor.ClientState, out
|
||||||
.hardness(5.0f)
|
.hardness(5.0f)
|
||||||
.resistance(6.0f)
|
.resistance(6.0f)
|
||||||
|
|
||||||
val portLayout: Array<PhysicalPort>
|
/** The cuboid that the main part of the block should fill. */
|
||||||
|
val cuboid: Box
|
||||||
|
get() = Cuboids.fullBlock
|
||||||
|
val portLayout: PhysicalPortLayout
|
||||||
|
val faceTextures: FaceTextures
|
||||||
|
get() = FaceTextures.withFrontAndSide { id }
|
||||||
|
|
||||||
fun onClientLoad(world: ClientWorld): CS
|
fun onClientLoad(world: ClientWorld): CS
|
||||||
fun onClientUnload(state: @UnsafeVariance CS, world: ClientWorld) {}
|
fun onClientUnload(state: @UnsafeVariance CS, world: ClientWorld) {}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,20 @@
|
||||||
package net.liquidev.dawd3.block.device
|
package net.liquidev.dawd3.block.device
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder
|
import net.fabricmc.fabric.api.`object`.builder.v1.block.entity.FabricBlockEntityTypeBuilder
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.audio.device.Devices
|
||||||
|
import net.liquidev.dawd3.audio.device.InputPortName
|
||||||
|
import net.liquidev.dawd3.audio.device.OutputPortName
|
||||||
|
import net.liquidev.dawd3.audio.device.PortName
|
||||||
import net.liquidev.dawd3.block.Blocks
|
import net.liquidev.dawd3.block.Blocks
|
||||||
import net.liquidev.dawd3.block.entity.D3BlockEntity
|
import net.liquidev.dawd3.block.entity.D3BlockEntity
|
||||||
import net.minecraft.block.BlockState
|
import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.entity.BlockEntityType
|
import net.minecraft.block.entity.BlockEntityType
|
||||||
import net.minecraft.client.world.ClientWorld
|
import net.minecraft.client.world.ClientWorld
|
||||||
|
import net.minecraft.nbt.NbtCompound
|
||||||
|
import net.minecraft.nbt.NbtElement
|
||||||
|
import net.minecraft.nbt.NbtHelper
|
||||||
|
import net.minecraft.nbt.NbtList
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
|
|
||||||
private typealias DeviceBlockFactory = FabricBlockEntityTypeBuilder.Factory<DeviceBlockEntity>
|
private typealias DeviceBlockFactory = FabricBlockEntityTypeBuilder.Factory<DeviceBlockEntity>
|
||||||
|
|
@ -14,19 +23,64 @@ class DeviceBlockEntity(
|
||||||
type: BlockEntityType<DeviceBlockEntity>,
|
type: BlockEntityType<DeviceBlockEntity>,
|
||||||
blockPos: BlockPos,
|
blockPos: BlockPos,
|
||||||
blockState: BlockState,
|
blockState: BlockState,
|
||||||
private val descriptor: AnyDeviceBlockDescriptor,
|
val descriptor: AnyDeviceBlockDescriptor,
|
||||||
) : D3BlockEntity(type, blockPos, blockState) {
|
) : D3BlockEntity(type, blockPos, blockState) {
|
||||||
companion object {
|
|
||||||
fun factory(descriptor: AnyDeviceBlockDescriptor): DeviceBlockFactory =
|
|
||||||
DeviceBlockFactory { blockPos, blockState ->
|
|
||||||
val type by lazy { Blocks.deviceBlocks[descriptor.id]!!.blockEntity }
|
|
||||||
DeviceBlockEntity(type, blockPos, blockState, descriptor)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var clientState: DeviceBlockDescriptor.ClientState? = null
|
private var clientState: DeviceBlockDescriptor.ClientState? = null
|
||||||
private var serverState: Any? = null
|
private var serverState: Any? = null
|
||||||
|
|
||||||
|
internal data class InputConnection(
|
||||||
|
val blockPosition: BlockPos,
|
||||||
|
val outputPortName: OutputPortName,
|
||||||
|
val color: Byte,
|
||||||
|
)
|
||||||
|
|
||||||
|
internal val inputConnections = hashMapOf<InputPortName, InputConnection>()
|
||||||
|
|
||||||
|
override fun readNbt(nbt: NbtCompound) {
|
||||||
|
super.readNbt(nbt)
|
||||||
|
|
||||||
|
val nbtConnections = nbt.getList("connections", NbtElement.COMPOUND_TYPE.toInt())
|
||||||
|
|
||||||
|
for (i in 0 until nbtConnections.size) {
|
||||||
|
val connectionNbt = nbtConnections.getCompound(i)
|
||||||
|
|
||||||
|
val outputString = connectionNbt.getString("output")
|
||||||
|
val output = PortName.fromString(outputString) ?: continue
|
||||||
|
if (output !is OutputPortName) {
|
||||||
|
logger.error("NBT declares 'output' field that refers to an input port")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val inputString = connectionNbt.getString("input")
|
||||||
|
val input = PortName.fromString(inputString) ?: continue
|
||||||
|
if (input !is InputPortName) {
|
||||||
|
logger.error("NBT declares 'input' field that refers to an output port")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
val blockPosition = NbtHelper.toBlockPos(connectionNbt.getCompound("position"))
|
||||||
|
val color = connectionNbt.getByte("color")
|
||||||
|
|
||||||
|
inputConnections[input] = InputConnection(blockPosition, outputPortName = output, color)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun writeNbt(nbt: NbtCompound) {
|
||||||
|
super.writeNbt(nbt)
|
||||||
|
|
||||||
|
val connectionsNbt = NbtList()
|
||||||
|
for ((inputPortName, connection) in inputConnections) {
|
||||||
|
val connectionNbt = NbtCompound()
|
||||||
|
connectionNbt.putString("output", connection.outputPortName.toString())
|
||||||
|
connectionNbt.putString("input", inputPortName.toString())
|
||||||
|
connectionNbt.put("position", NbtHelper.fromBlockPos(connection.blockPosition))
|
||||||
|
connectionNbt.putByte("color", connection.color)
|
||||||
|
connectionsNbt.add(connectionNbt)
|
||||||
|
}
|
||||||
|
nbt.put("connections", connectionsNbt)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onClientLoad(world: ClientWorld) {
|
override fun onClientLoad(world: ClientWorld) {
|
||||||
clientState = descriptor.onClientLoad(world)
|
clientState = descriptor.onClientLoad(world)
|
||||||
}
|
}
|
||||||
|
|
@ -35,6 +89,72 @@ class DeviceBlockEntity(
|
||||||
val clientState = clientState
|
val clientState = clientState
|
||||||
if (clientState != null) {
|
if (clientState != null) {
|
||||||
descriptor.onClientUnload(clientState, world)
|
descriptor.onClientUnload(clientState, world)
|
||||||
|
Devices.severAllConnectionsInDevice(clientState.logicalDevice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun reapInvalidConnections() {
|
||||||
|
inputConnections.keys.removeAll {
|
||||||
|
descriptor.portLayout[it] == null
|
||||||
|
}
|
||||||
|
val world = world
|
||||||
|
if (world != null) {
|
||||||
|
inputConnections.values.removeAll {
|
||||||
|
world.getBlockEntity(it.blockPosition) !is DeviceBlockEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val logger = Mod.logger<DeviceBlockEntity>()
|
||||||
|
|
||||||
|
fun factory(descriptor: AnyDeviceBlockDescriptor): DeviceBlockFactory =
|
||||||
|
DeviceBlockFactory { blockPos, blockState ->
|
||||||
|
val type by lazy { Blocks.deviceBlocks[descriptor.id]!!.blockEntity }
|
||||||
|
DeviceBlockEntity(type, blockPos, blockState, descriptor)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun connectLogicalDevices(
|
||||||
|
fromBlockEntity: DeviceBlockEntity,
|
||||||
|
fromPort: PortName,
|
||||||
|
toBlockEntity: DeviceBlockEntity,
|
||||||
|
toPort: PortName,
|
||||||
|
cableColor: Byte,
|
||||||
|
) {
|
||||||
|
val (outputBlockEntity, outputPort, inputBlockEntity, inputPort) = Devices.sortPortsByInputAndOutput(
|
||||||
|
fromBlockEntity,
|
||||||
|
fromPort,
|
||||||
|
toBlockEntity,
|
||||||
|
toPort
|
||||||
|
) ?: throw PortDirectionException("connected ports must be of opposing directions")
|
||||||
|
val outputDevice = outputBlockEntity.clientState?.logicalDevice ?: return
|
||||||
|
val inputDevice = inputBlockEntity.clientState?.logicalDevice ?: return
|
||||||
|
inputBlockEntity.inputConnections[inputPort] =
|
||||||
|
InputConnection(outputBlockEntity.pos, outputPort, cableColor)
|
||||||
|
Devices.makeConnection(
|
||||||
|
outputDevice,
|
||||||
|
outputPort.resolveInstance(),
|
||||||
|
inputDevice,
|
||||||
|
inputPort
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun connectPhysicalDevices(
|
||||||
|
fromBlockEntity: DeviceBlockEntity,
|
||||||
|
fromPort: PortName,
|
||||||
|
toBlockEntity: DeviceBlockEntity,
|
||||||
|
toPort: PortName,
|
||||||
|
cableColor: Byte,
|
||||||
|
) {
|
||||||
|
val (outputBlockEntity, outputPort, inputBlockEntity, inputPort) = Devices.sortPortsByInputAndOutput(
|
||||||
|
fromBlockEntity,
|
||||||
|
fromPort,
|
||||||
|
toBlockEntity,
|
||||||
|
toPort,
|
||||||
|
) ?: throw PortDirectionException("connected ports must be of opposing directions")
|
||||||
|
inputBlockEntity.inputConnections[inputPort] =
|
||||||
|
InputConnection(outputBlockEntity.pos, outputPort, cableColor)
|
||||||
|
outputBlockEntity.markDirty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,401 @@
|
||||||
|
package net.liquidev.dawd3.block.device
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.block.Blocks
|
||||||
|
import net.liquidev.dawd3.common.*
|
||||||
|
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
||||||
|
import net.liquidev.dawd3.item.PatchCableItem
|
||||||
|
import net.minecraft.block.BlockState
|
||||||
|
import net.minecraft.client.render.*
|
||||||
|
import net.minecraft.client.render.RenderLayer.MultiPhaseParameters
|
||||||
|
import net.minecraft.client.render.block.entity.BlockEntityRenderer
|
||||||
|
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory
|
||||||
|
import net.minecraft.client.util.SpriteIdentifier
|
||||||
|
import net.minecraft.client.util.math.MatrixStack
|
||||||
|
import net.minecraft.screen.PlayerScreenHandler
|
||||||
|
import net.minecraft.state.property.Properties
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.util.math.Quaternion
|
||||||
|
import net.minecraft.util.math.Vec3d
|
||||||
|
import net.minecraft.util.math.Vec3f
|
||||||
|
import net.minecraft.world.World
|
||||||
|
import java.util.function.Function
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
class DeviceBlockEntityRenderer(context: BlockEntityRendererFactory.Context) : BlockEntityRenderer<DeviceBlockEntity> {
|
||||||
|
private companion object {
|
||||||
|
val logger = Mod.logger<DeviceBlockEntityRenderer>()
|
||||||
|
|
||||||
|
val renderLayer: RenderLayer = RenderLayer.of(
|
||||||
|
"solid",
|
||||||
|
VertexFormats.POSITION_COLOR_TEXTURE_LIGHT,
|
||||||
|
VertexFormat.DrawMode.TRIANGLE_STRIP,
|
||||||
|
2048,
|
||||||
|
MultiPhaseParameters.builder()
|
||||||
|
.lightmap(RenderPhase.ENABLE_LIGHTMAP)
|
||||||
|
.shader(RenderPhase.SOLID_SHADER)
|
||||||
|
.texture(RenderPhase.BLOCK_ATLAS_TEXTURE)
|
||||||
|
.cull(RenderPhase.DISABLE_CULLING)
|
||||||
|
.build(true)
|
||||||
|
)
|
||||||
|
val renderLayerFactory = Function<Identifier, RenderLayer> { renderLayer }
|
||||||
|
|
||||||
|
const val cableThickness = 0.03f
|
||||||
|
const val cableSegmentCount = 6
|
||||||
|
const val cableSag = 0.2f
|
||||||
|
val cableColorsSprite = SpriteIdentifier(
|
||||||
|
PlayerScreenHandler.BLOCK_ATLAS_TEXTURE,
|
||||||
|
Identifier(Mod.id, "device/cable")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val blockRenderer = context.renderManager
|
||||||
|
private val patchCablePlug = Blocks.patchCablePlug.defaultState
|
||||||
|
|
||||||
|
override fun rendersOutsideBoundingBox(blockEntity: DeviceBlockEntity): Boolean {
|
||||||
|
// TODO: This needs to return true if the block entity has cables attached.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun render(
|
||||||
|
blockEntity: DeviceBlockEntity,
|
||||||
|
tickDelta: Float,
|
||||||
|
matrixStack: MatrixStack,
|
||||||
|
vertexConsumers: VertexConsumerProvider,
|
||||||
|
light: Int,
|
||||||
|
overlay: Int,
|
||||||
|
) {
|
||||||
|
val world = blockEntity.world ?: return
|
||||||
|
val blockState = world.getBlockState(blockEntity.pos)
|
||||||
|
if (blockState.block !is DeviceBlock) {
|
||||||
|
// render() gets called for a frame while the block is still air after it's destroyed.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
renderPlugsInsidePorts(
|
||||||
|
world,
|
||||||
|
blockState,
|
||||||
|
blockEntity,
|
||||||
|
matrixStack,
|
||||||
|
vertexConsumers,
|
||||||
|
overlay
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun rotateToFaceFront(
|
||||||
|
blockState: BlockState,
|
||||||
|
matrices: MatrixStack,
|
||||||
|
) {
|
||||||
|
// Choose center as the transform origin. That way we can rotate by kπ/2 to get all the
|
||||||
|
// possible horizontal orientations of the block.
|
||||||
|
matrices.translate(0.5, 0.0, 0.5)
|
||||||
|
val facing = HorizontalDirection.fromDirection(blockState[Properties.HORIZONTAL_FACING])!!
|
||||||
|
matrices.multiply(Quaternion.fromEulerXyz(0f, -facing.angle, 0f))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderPlugsInsidePorts(
|
||||||
|
world: World,
|
||||||
|
blockState: BlockState,
|
||||||
|
blockEntity: DeviceBlockEntity,
|
||||||
|
matrixStack: MatrixStack,
|
||||||
|
vertexConsumers: VertexConsumerProvider,
|
||||||
|
overlay: Int,
|
||||||
|
) {
|
||||||
|
val facing = HorizontalDirection.fromDirection(blockState[Properties.HORIZONTAL_FACING])!!
|
||||||
|
|
||||||
|
matrixStack.push()
|
||||||
|
rotateToFaceFront(blockState, matrixStack)
|
||||||
|
for ((_, connection) in PatchCableItem.ongoingConnections) {
|
||||||
|
if (connection.blockPosition == blockEntity.pos) {
|
||||||
|
val port = blockEntity.descriptor.portLayout[connection.portName]
|
||||||
|
if (port != null) {
|
||||||
|
renderPlugInsidePort(
|
||||||
|
world, blockEntity, facing, port, matrixStack, vertexConsumers, overlay
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
logger.error("ongoing connection references port name '${connection.portName.id}' which does not exist on the block entity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: There's probably a better place to call this.
|
||||||
|
// Maybe we should actually not call this at all here?
|
||||||
|
// I'm worried that for a frame a device could be something other than DeviceBlockEntity
|
||||||
|
// and we'll get plugs without cables rendering permanently. Which would be bad.
|
||||||
|
blockEntity.reapInvalidConnections()
|
||||||
|
|
||||||
|
for ((portName, _) in blockEntity.inputConnections) {
|
||||||
|
val inputPort = blockEntity.descriptor.portLayout[portName]!!
|
||||||
|
renderPlugInsidePort(
|
||||||
|
world, blockEntity, facing, inputPort, matrixStack, vertexConsumers, overlay
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
matrixStack.pop()
|
||||||
|
|
||||||
|
for ((_, connection) in blockEntity.inputConnections) {
|
||||||
|
val outputBlockEntity =
|
||||||
|
world.getBlockEntity(connection.blockPosition) as DeviceBlockEntity
|
||||||
|
val outputBlockState = world.getBlockState(connection.blockPosition)
|
||||||
|
val outputPort = outputBlockEntity.descriptor.portLayout[connection.outputPortName]
|
||||||
|
if (outputPort != null) {
|
||||||
|
val delta = (outputBlockEntity.pos - blockEntity.pos).toVec3d()
|
||||||
|
matrixStack.push()
|
||||||
|
matrixStack.translate(delta.x, delta.y, delta.z)
|
||||||
|
rotateToFaceFront(outputBlockState, matrixStack)
|
||||||
|
renderPlugInsidePort(
|
||||||
|
world,
|
||||||
|
outputBlockEntity,
|
||||||
|
facing,
|
||||||
|
outputPort,
|
||||||
|
matrixStack,
|
||||||
|
vertexConsumers,
|
||||||
|
overlay
|
||||||
|
)
|
||||||
|
matrixStack.pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for ((inputPortName, connection) in blockEntity.inputConnections) {
|
||||||
|
val startAbsolute = blockEntity.pos.toVec3d()
|
||||||
|
val endAbsolute = connection.blockPosition.toVec3d()
|
||||||
|
|
||||||
|
// TODO: Null checks here?
|
||||||
|
val inputPort = blockEntity.descriptor.portLayout[inputPortName]!!
|
||||||
|
val outputBlockState = world.getBlockState(connection.blockPosition)
|
||||||
|
// TODO: The line below will crash if the block is destroyed (becomes air)
|
||||||
|
// Though it never crashed for me during testing?
|
||||||
|
val outputBlockFacing =
|
||||||
|
HorizontalDirection.fromDirection(outputBlockState[Properties.HORIZONTAL_FACING])!!
|
||||||
|
val outputBlockEntity =
|
||||||
|
world.getBlockEntity(connection.blockPosition) as DeviceBlockEntity
|
||||||
|
val outputPort = outputBlockEntity.descriptor.portLayout[connection.outputPortName]!!
|
||||||
|
|
||||||
|
val protrusionAmount = 0.69f // nice
|
||||||
|
val start = inputPort.blockCablePosition(facing, protrusionAmount)
|
||||||
|
val end = outputPort.blockCablePosition(outputBlockFacing, protrusionAmount)
|
||||||
|
val delta = (endAbsolute - startAbsolute).toVec3f() + end
|
||||||
|
|
||||||
|
renderCable(
|
||||||
|
world,
|
||||||
|
matrixStack,
|
||||||
|
vertexConsumers,
|
||||||
|
worldFrom = blockEntity.pos.toVec3d(),
|
||||||
|
start,
|
||||||
|
delta,
|
||||||
|
connection.color.toFloat()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getFacingInWorldSpace(
|
||||||
|
blockFacing: HorizontalDirection,
|
||||||
|
portFacing: HorizontalDirection,
|
||||||
|
) =
|
||||||
|
blockFacing + portFacing - HorizontalDirection.North
|
||||||
|
|
||||||
|
private fun renderPlugInsidePort(
|
||||||
|
world: World,
|
||||||
|
blockEntity: DeviceBlockEntity,
|
||||||
|
blockFacing: HorizontalDirection,
|
||||||
|
port: PhysicalPort,
|
||||||
|
matrices: MatrixStack,
|
||||||
|
vertexConsumers: VertexConsumerProvider,
|
||||||
|
overlay: Int,
|
||||||
|
) {
|
||||||
|
val positionOnSide =
|
||||||
|
(DeviceBlockModel.relativeToAbsolutePortPosition(port.position) + DeviceBlockModel.portSize / 2f) / 16f
|
||||||
|
val face = port.side.face
|
||||||
|
matrices.push()
|
||||||
|
matrices.multiply(
|
||||||
|
Quaternion.fromEulerXyz(0f, face.angle, 0f)
|
||||||
|
)
|
||||||
|
matrices.translate(1f - positionOnSide.x.toDouble(), 1f - positionOnSide.y.toDouble(), 0.0)
|
||||||
|
|
||||||
|
val facing = getFacingInWorldSpace(blockFacing, face)
|
||||||
|
val light =
|
||||||
|
WorldRenderer.getLightmapCoordinates(world, blockEntity.pos.add(facing.vector))
|
||||||
|
blockRenderer.renderBlockAsEntity(
|
||||||
|
patchCablePlug, matrices, vertexConsumers, light, overlay
|
||||||
|
)
|
||||||
|
|
||||||
|
matrices.pop()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun catenary(sag: Float, t: Float): Float {
|
||||||
|
// Not an actual catenary, but close enough.
|
||||||
|
val u = 2f * t - 1f
|
||||||
|
return sag * (u * u - 1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderCable(
|
||||||
|
world: World,
|
||||||
|
matrixStack: MatrixStack,
|
||||||
|
vertexConsumers: VertexConsumerProvider,
|
||||||
|
worldFrom: Vec3d,
|
||||||
|
from: Vec3f,
|
||||||
|
to: Vec3f,
|
||||||
|
colorIndex: Float,
|
||||||
|
) {
|
||||||
|
val forward = to - from
|
||||||
|
val right = forward.copy()
|
||||||
|
right.cross(Vec3f.POSITIVE_Y)
|
||||||
|
right.normalize()
|
||||||
|
right.multiplyComponentwise(cableThickness, cableThickness, cableThickness)
|
||||||
|
val up = right.copy()
|
||||||
|
up.cross(forward)
|
||||||
|
up.normalize()
|
||||||
|
up.multiplyComponentwise(cableThickness, cableThickness, cableThickness)
|
||||||
|
renderCableWithThicknessVector(
|
||||||
|
world,
|
||||||
|
matrixStack,
|
||||||
|
vertexConsumers,
|
||||||
|
worldFrom,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
thicknessVector = up,
|
||||||
|
colorIndex
|
||||||
|
)
|
||||||
|
renderCableWithThicknessVector(
|
||||||
|
world,
|
||||||
|
matrixStack,
|
||||||
|
vertexConsumers,
|
||||||
|
worldFrom,
|
||||||
|
from,
|
||||||
|
to,
|
||||||
|
thicknessVector = right,
|
||||||
|
colorIndex
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun renderCableWithThicknessVector(
|
||||||
|
world: World,
|
||||||
|
matrixStack: MatrixStack,
|
||||||
|
vertexConsumers: VertexConsumerProvider,
|
||||||
|
worldFrom: Vec3d,
|
||||||
|
from: Vec3f,
|
||||||
|
to: Vec3f,
|
||||||
|
thicknessVector: Vec3f,
|
||||||
|
colorIndex: Float,
|
||||||
|
) {
|
||||||
|
val vertexBuffer = cableColorsSprite.getVertexConsumer(
|
||||||
|
vertexConsumers,
|
||||||
|
renderLayerFactory
|
||||||
|
) as SpriteTexturedVertexConsumer
|
||||||
|
val matrices = matrixStack.peek()
|
||||||
|
// ↑ Cast needed due to different interface method resolution rules in Kotlin.
|
||||||
|
// See comment in addCableSegment.
|
||||||
|
|
||||||
|
val uLeft = colorIndex / 16f
|
||||||
|
val uRight = (colorIndex + 1) / 16f
|
||||||
|
|
||||||
|
addCableSegment(
|
||||||
|
world,
|
||||||
|
matrices,
|
||||||
|
vertexBuffer,
|
||||||
|
worldFrom.x,
|
||||||
|
worldFrom.y,
|
||||||
|
worldFrom.z,
|
||||||
|
from.x,
|
||||||
|
from.y,
|
||||||
|
from.z,
|
||||||
|
thicknessVector,
|
||||||
|
uLeft,
|
||||||
|
uRight,
|
||||||
|
v = 0f,
|
||||||
|
)
|
||||||
|
|
||||||
|
val sag = cableSag + abs(to.y - from.y) * 0.2f
|
||||||
|
for (i in 1 until cableSegmentCount) {
|
||||||
|
val t = i.toFloat() / cableSegmentCount.toFloat()
|
||||||
|
val sagOffset = catenary(sag, t)
|
||||||
|
val x = lerp(from.x, to.x, t)
|
||||||
|
val y = lerp(from.y, to.y, t) + sagOffset
|
||||||
|
val z = lerp(from.z, to.z, t)
|
||||||
|
addCableSegment(
|
||||||
|
world,
|
||||||
|
matrices,
|
||||||
|
vertexBuffer,
|
||||||
|
worldFrom.x,
|
||||||
|
worldFrom.y,
|
||||||
|
worldFrom.z,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
z,
|
||||||
|
thicknessVector,
|
||||||
|
uLeft,
|
||||||
|
uRight,
|
||||||
|
v = t,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
addCableSegment(
|
||||||
|
world,
|
||||||
|
matrices,
|
||||||
|
vertexBuffer,
|
||||||
|
worldFrom.x,
|
||||||
|
worldFrom.y,
|
||||||
|
worldFrom.z,
|
||||||
|
to.x,
|
||||||
|
to.y,
|
||||||
|
to.z,
|
||||||
|
thicknessVector,
|
||||||
|
uLeft,
|
||||||
|
uRight,
|
||||||
|
v = 1f,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addCableSegment(
|
||||||
|
world: World,
|
||||||
|
matrices: MatrixStack.Entry,
|
||||||
|
vertexBuffer: SpriteTexturedVertexConsumer,
|
||||||
|
originX: Double,
|
||||||
|
originY: Double,
|
||||||
|
originZ: Double,
|
||||||
|
x: Float,
|
||||||
|
y: Float,
|
||||||
|
z: Float,
|
||||||
|
thickness: Vec3f,
|
||||||
|
uLeft: Float,
|
||||||
|
uRight: Float,
|
||||||
|
v: Float,
|
||||||
|
) {
|
||||||
|
val blockPosition = BlockPos(
|
||||||
|
floor(originX + x.toDouble()).toInt(),
|
||||||
|
floor(originY + y.toDouble()).toInt(),
|
||||||
|
floor(originZ + z.toDouble()).toInt(),
|
||||||
|
)
|
||||||
|
val light =
|
||||||
|
WorldRenderer.getLightmapCoordinates(world, blockPosition)
|
||||||
|
|
||||||
|
// NOTE: Unlike in Java, where we can chain these method calls, in Kotlin we have to
|
||||||
|
// specify the receiver explicitly for each one. This is because Kotlin resolves interface
|
||||||
|
// methods a little differently: instead of always resolving dynamically, it will instead
|
||||||
|
// always resolve default methods statically. Which is not what we want here, because that
|
||||||
|
// gives us bad UVs.
|
||||||
|
vertexBuffer.vertex(
|
||||||
|
matrices.positionMatrix,
|
||||||
|
x - thickness.x,
|
||||||
|
y - thickness.y,
|
||||||
|
z - thickness.z,
|
||||||
|
)
|
||||||
|
vertexBuffer.color(255, 255, 255, 255)
|
||||||
|
vertexBuffer.texture(uLeft, v)
|
||||||
|
vertexBuffer.light(light)
|
||||||
|
vertexBuffer.next()
|
||||||
|
|
||||||
|
vertexBuffer.vertex(
|
||||||
|
matrices.positionMatrix,
|
||||||
|
x + thickness.x,
|
||||||
|
y + thickness.y,
|
||||||
|
z + thickness.z,
|
||||||
|
)
|
||||||
|
vertexBuffer.color(255, 255, 255, 255)
|
||||||
|
vertexBuffer.texture(uRight, v)
|
||||||
|
vertexBuffer.light(light)
|
||||||
|
vertexBuffer.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,31 +1,82 @@
|
||||||
package net.liquidev.dawd3.block.device
|
package net.liquidev.dawd3.block.device
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.audio.device.OutputPortName
|
||||||
import net.liquidev.dawd3.audio.device.PortName
|
import net.liquidev.dawd3.audio.device.PortName
|
||||||
import net.liquidev.dawd3.common.Affine2x2
|
import net.liquidev.dawd3.common.Affine2x2f
|
||||||
import net.minecraft.util.math.Direction
|
import net.liquidev.dawd3.common.HorizontalDirection
|
||||||
|
import net.liquidev.dawd3.common.div
|
||||||
|
import net.liquidev.dawd3.common.plus
|
||||||
|
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
||||||
import net.minecraft.util.math.Vec2f
|
import net.minecraft.util.math.Vec2f
|
||||||
|
import net.minecraft.util.math.Vec3f
|
||||||
|
|
||||||
/** The physical appearance of a port. */
|
/** The physical appearance of a port. */
|
||||||
data class PhysicalPort(
|
data class PhysicalPort(
|
||||||
val port: PortName,
|
|
||||||
/** The coordinates are relative, in range [0.0, 1.0] where 0 is top-left and 1 is bottom-right. */
|
/** The coordinates are relative, in range [0.0, 1.0] where 0 is top-left and 1 is bottom-right. */
|
||||||
val position: Vec2f,
|
val position: Vec2f,
|
||||||
val side: Side,
|
val side: Side,
|
||||||
|
val logicalPort: PortName,
|
||||||
) {
|
) {
|
||||||
/** Where to place the port on the model. */
|
/** Where to place the port on the model. */
|
||||||
enum class Side(
|
enum class Side(
|
||||||
/** The transform matrix to apply to ports' coordinates in the model. */
|
/** The transform matrix to apply to ports' coordinates in the model. */
|
||||||
val transform: Affine2x2,
|
val modelTransform: Affine2x2f,
|
||||||
/** The model face on which the port is placed. */
|
/** The model face on which the port is placed. */
|
||||||
val face: Direction,
|
val face: HorizontalDirection,
|
||||||
) {
|
) {
|
||||||
Front(
|
Front(
|
||||||
transform = Affine2x2(1f, 0f, 0f, 1f),
|
modelTransform = Affine2x2f(1f, 0f, 0f, 1f),
|
||||||
face = Direction.NORTH,
|
face = HorizontalDirection.North,
|
||||||
),
|
),
|
||||||
Back(
|
Back(
|
||||||
transform = Affine2x2(-1f, 0f, 0f, -1f, translateX = 16f, translateY = 16f),
|
modelTransform = Affine2x2f(-1f, 0f, 0f, -1f, translateX = 16f, translateY = 16f),
|
||||||
face = Direction.SOUTH,
|
face = HorizontalDirection.South,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Where the port's patch cable cord is located relative to a block's center. */
|
||||||
|
fun blockCablePosition(
|
||||||
|
blockFacing: HorizontalDirection,
|
||||||
|
protrusionAmount: Float,
|
||||||
|
): Vec3f {
|
||||||
|
val facing = (blockFacing + side.face).clockwise()
|
||||||
|
val absolutePosition =
|
||||||
|
DeviceBlockModel.relativeToAbsolutePortPosition(position) + DeviceBlockModel.portSize / 2f
|
||||||
|
val invertedPosition = Vec2f(1f - absolutePosition.x / 16f, 1f - absolutePosition.y / 16f)
|
||||||
|
val forward = Vec3f(protrusionAmount, invertedPosition.y - 0.5f, invertedPosition.x - 0.5f)
|
||||||
|
return facing.rotateY(forward) + Vec3f(0.5f, 0.5f, 0.5f)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
class LayoutBuilder {
|
||||||
|
internal val layout = PhysicalPortLayout()
|
||||||
|
|
||||||
|
fun port(portName: PortName, physicalPort: PhysicalPort) {
|
||||||
|
layout[portName] = physicalPort
|
||||||
|
}
|
||||||
|
|
||||||
|
fun port(portName: PortName, position: Vec2f, side: Side) {
|
||||||
|
port(portName, PhysicalPort(position, side, portName))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can be used to create physical ports whose name is different from the logical
|
||||||
|
* port name, to allow for aliasing multiple physical ports onto a single logical port.
|
||||||
|
*
|
||||||
|
* Note that this is only allowed for output ports, because input ports cannot have
|
||||||
|
* multiple signals going into them.
|
||||||
|
*/
|
||||||
|
fun port(portName: OutputPortName, instanceName: String, position: Vec2f, side: Side) {
|
||||||
|
port(portName.makeInstanced(instanceName), PhysicalPort(position, side, portName))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun layout(build: LayoutBuilder.() -> Unit): PhysicalPortLayout {
|
||||||
|
val builder = LayoutBuilder()
|
||||||
|
build(builder)
|
||||||
|
return builder.layout
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias PhysicalPortLayout = HashMap<PortName, PhysicalPort>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
|
||||||
|
interface FaceTextures {
|
||||||
|
val front: Identifier
|
||||||
|
val back: Identifier
|
||||||
|
val left: Identifier
|
||||||
|
val right: Identifier
|
||||||
|
val top: Identifier
|
||||||
|
val bottom: Identifier
|
||||||
|
|
||||||
|
val particle: Identifier
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun withFrontAndSide(lazyId: () -> Identifier): FaceTextures = object : FaceTextures {
|
||||||
|
private val id get() = lazyId()
|
||||||
|
private val side = Identifier(id.namespace, "block/${id.path}_side")
|
||||||
|
|
||||||
|
override val front = Identifier(id.namespace, "block/${id.path}_front")
|
||||||
|
override val back = side
|
||||||
|
override val left = side
|
||||||
|
override val right = side
|
||||||
|
override val top = side
|
||||||
|
override val bottom = side
|
||||||
|
override val particle = side
|
||||||
|
}
|
||||||
|
|
||||||
|
fun withTopSideAndBottom(lazyId: () -> Identifier): FaceTextures = object : FaceTextures {
|
||||||
|
private val id get() = lazyId()
|
||||||
|
private val side = Identifier(id.namespace, "block/${id.path}_side")
|
||||||
|
|
||||||
|
override val front = side
|
||||||
|
override val back = side
|
||||||
|
override val left = side
|
||||||
|
override val right = side
|
||||||
|
override val top = Identifier(id.namespace, "block/${id.path}_top")
|
||||||
|
override val bottom = Identifier(id.namespace, "block/${id.path}_bottom")
|
||||||
|
override val particle = side
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
package net.liquidev.dawd3.block.device
|
||||||
|
|
||||||
|
class PortDirectionException(what: String) : Exception(what)
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package net.liquidev.dawd3.block.devices
|
||||||
|
|
||||||
|
import FaceTextures
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.audio.device.DeviceInstance
|
||||||
|
import net.liquidev.dawd3.audio.devices.ConstantDevice
|
||||||
|
import net.liquidev.dawd3.block.device.DeviceBlockDescriptor
|
||||||
|
import net.liquidev.dawd3.block.device.PhysicalPort
|
||||||
|
import net.liquidev.dawd3.common.Cuboids
|
||||||
|
import net.minecraft.client.world.ClientWorld
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.math.Vec2f
|
||||||
|
|
||||||
|
object FaderBlockDescriptor : DeviceBlockDescriptor<FaderBlockDescriptor.ClientState, Unit> {
|
||||||
|
override val id = Identifier(Mod.id, "fader")
|
||||||
|
|
||||||
|
override val cuboid = Cuboids.halfBlock
|
||||||
|
override val portLayout = PhysicalPort.layout {
|
||||||
|
port(
|
||||||
|
ConstantDevice.outputPort,
|
||||||
|
instanceName = "front",
|
||||||
|
position = Vec2f(0.5f, 0.75f),
|
||||||
|
side = PhysicalPort.Side.Front,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override val faceTextures = FaceTextures.withTopSideAndBottom { id }
|
||||||
|
|
||||||
|
class ClientState : DeviceBlockDescriptor.ClientState {
|
||||||
|
override val logicalDevice = DeviceInstance(ConstantDevice(value = 440.0f))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClientLoad(world: ClientWorld) = ClientState()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package net.liquidev.dawd3.block.devices
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.audio.device.DeviceInstance
|
||||||
|
import net.liquidev.dawd3.audio.devices.SineOscillatorDevice
|
||||||
|
import net.liquidev.dawd3.block.device.DeviceBlockDescriptor
|
||||||
|
import net.liquidev.dawd3.block.device.PhysicalPort
|
||||||
|
import net.minecraft.client.world.ClientWorld
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.math.Vec2f
|
||||||
|
|
||||||
|
object SineOscillatorBlockDescriptor : DeviceBlockDescriptor<SineOscillatorBlockDescriptor.ClientState, Unit> {
|
||||||
|
override val id = Identifier(Mod.id, "sine_oscillator")
|
||||||
|
|
||||||
|
override val portLayout = PhysicalPort.layout {
|
||||||
|
port(
|
||||||
|
SineOscillatorDevice.frequencyCVPort,
|
||||||
|
position = Vec2f(0.25f, 0.5f),
|
||||||
|
side = PhysicalPort.Side.Front,
|
||||||
|
)
|
||||||
|
port(
|
||||||
|
SineOscillatorDevice.outputPort,
|
||||||
|
position = Vec2f(0.75f, 0.5f),
|
||||||
|
side = PhysicalPort.Side.Front
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ClientState : DeviceBlockDescriptor.ClientState {
|
||||||
|
override val logicalDevice = DeviceInstance(SineOscillatorDevice())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onClientLoad(world: ClientWorld) = ClientState()
|
||||||
|
}
|
||||||
|
|
@ -22,9 +22,9 @@ object SpeakerBlockDescriptor : DeviceBlockDescriptor<SpeakerBlockDescriptor.Cli
|
||||||
.hardness(2.0f).resistance(6.0f)
|
.hardness(2.0f).resistance(6.0f)
|
||||||
.sounds(BlockSoundGroup.WOOD)!!
|
.sounds(BlockSoundGroup.WOOD)!!
|
||||||
|
|
||||||
override val portLayout = arrayOf(
|
override val portLayout = PhysicalPort.layout {
|
||||||
PhysicalPort(TerminalDevice.Input, Vec2f(0.5f, 0.5f), PhysicalPort.Side.Back)
|
port(TerminalDevice.inputPort, position = Vec2f(0.5f, 0.75f), side = PhysicalPort.Side.Back)
|
||||||
)
|
}
|
||||||
|
|
||||||
class ClientState : DeviceBlockDescriptor.ClientState {
|
class ClientState : DeviceBlockDescriptor.ClientState {
|
||||||
internal val channel: MixGenerator.Channel<DeviceGraphGenerator>
|
internal val channel: MixGenerator.Channel<DeviceGraphGenerator>
|
||||||
|
|
@ -33,15 +33,12 @@ object SpeakerBlockDescriptor : DeviceBlockDescriptor<SpeakerBlockDescriptor.Cli
|
||||||
init {
|
init {
|
||||||
val generator = DeviceGraphGenerator()
|
val generator = DeviceGraphGenerator()
|
||||||
channel = Audio.mixer.createChannel(generator)
|
channel = Audio.mixer.createChannel(generator)
|
||||||
|
|
||||||
val terminal = generator.terminalDevice
|
val terminal = generator.terminalDevice
|
||||||
logicalDevice = terminal
|
logicalDevice = terminal
|
||||||
|
|
||||||
generator.rebuildSim()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onClientLoad(world: ClientWorld): ClientState = ClientState()
|
override fun onClientLoad(world: ClientWorld) = ClientState()
|
||||||
|
|
||||||
override fun onClientUnload(state: ClientState, world: ClientWorld) {
|
override fun onClientUnload(state: ClientState, world: ClientWorld) {
|
||||||
state.channel.stop()
|
state.channel.stop()
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,26 @@ import net.minecraft.block.BlockState
|
||||||
import net.minecraft.block.entity.BlockEntity
|
import net.minecraft.block.entity.BlockEntity
|
||||||
import net.minecraft.block.entity.BlockEntityType
|
import net.minecraft.block.entity.BlockEntityType
|
||||||
import net.minecraft.client.world.ClientWorld
|
import net.minecraft.client.world.ClientWorld
|
||||||
|
import net.minecraft.nbt.NbtCompound
|
||||||
|
import net.minecraft.network.Packet
|
||||||
|
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||||
|
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
|
||||||
import net.minecraft.util.math.BlockPos
|
import net.minecraft.util.math.BlockPos
|
||||||
|
|
||||||
abstract class D3BlockEntity(
|
abstract class D3BlockEntity(
|
||||||
type: BlockEntityType<out D3BlockEntity>,
|
type: BlockEntityType<out D3BlockEntity>,
|
||||||
pos: BlockPos,
|
pos: BlockPos,
|
||||||
state: BlockState,
|
state: BlockState,
|
||||||
) :
|
) : BlockEntity(type, pos, state) {
|
||||||
BlockEntity(type, pos, state) {
|
|
||||||
|
|
||||||
open fun onClientLoad(world: ClientWorld) {}
|
open fun onClientLoad(world: ClientWorld) {}
|
||||||
open fun onClientUnload(world: ClientWorld) {}
|
open fun onClientUnload(world: ClientWorld) {}
|
||||||
|
|
||||||
|
override fun toUpdatePacket(): Packet<ClientPlayPacketListener> {
|
||||||
|
return BlockEntityUpdateS2CPacket.create(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun toInitialChunkDataNbt(): NbtCompound {
|
||||||
|
return createNbt()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ package net.liquidev.dawd3.block.entity
|
||||||
|
|
||||||
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents
|
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents
|
||||||
|
|
||||||
fun registerBlockEntityEvents() {
|
fun registerClientBlockEntityEvents() {
|
||||||
ClientBlockEntityEvents.BLOCK_ENTITY_LOAD.register { blockEntity, world ->
|
ClientBlockEntityEvents.BLOCK_ENTITY_LOAD.register { blockEntity, world ->
|
||||||
if (blockEntity is D3BlockEntity) {
|
if (blockEntity is D3BlockEntity) {
|
||||||
blockEntity.onClientLoad(world)
|
blockEntity.onClientLoad(world)
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import net.minecraft.util.math.Vec2f
|
||||||
import net.minecraft.util.math.Vec3f
|
import net.minecraft.util.math.Vec3f
|
||||||
|
|
||||||
/** 2x2 matrix. */
|
/** 2x2 matrix. */
|
||||||
data class Affine2x2(
|
data class Affine2x2f(
|
||||||
val xx: Float,
|
val xx: Float,
|
||||||
val xy: Float,
|
val xy: Float,
|
||||||
val yx: Float,
|
val yx: Float,
|
||||||
20
src/main/kotlin/net/liquidev/dawd3/common/Mat2x3f.kt
Normal file
20
src/main/kotlin/net/liquidev/dawd3/common/Mat2x3f.kt
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package net.liquidev.dawd3.common
|
||||||
|
|
||||||
|
import net.minecraft.util.math.Vec2f
|
||||||
|
import net.minecraft.util.math.Vec3f
|
||||||
|
|
||||||
|
/** Matrix with two rows and three columns. */
|
||||||
|
class Mat2x3f(
|
||||||
|
val xx: Float,
|
||||||
|
val xy: Float,
|
||||||
|
val xz: Float,
|
||||||
|
val yx: Float,
|
||||||
|
val yy: Float,
|
||||||
|
val yz: Float,
|
||||||
|
) {
|
||||||
|
operator fun times(vec: Vec3f) =
|
||||||
|
Vec2f(
|
||||||
|
vec.x * xx + vec.y * xy + vec.z * xz,
|
||||||
|
vec.x * yx + vec.y * yy + vec.z * yz
|
||||||
|
)
|
||||||
|
}
|
||||||
21
src/main/kotlin/net/liquidev/dawd3/common/Mat3x2f.kt
Normal file
21
src/main/kotlin/net/liquidev/dawd3/common/Mat3x2f.kt
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
package net.liquidev.dawd3.common
|
||||||
|
|
||||||
|
import net.minecraft.util.math.Vec2f
|
||||||
|
import net.minecraft.util.math.Vec3f
|
||||||
|
|
||||||
|
/** Matrix with three columns and two rows. */
|
||||||
|
class Mat3x2f(
|
||||||
|
val xx: Float,
|
||||||
|
val xy: Float,
|
||||||
|
val yx: Float,
|
||||||
|
val yy: Float,
|
||||||
|
val zx: Float,
|
||||||
|
val zy: Float,
|
||||||
|
) {
|
||||||
|
operator fun times(vec: Vec2f): Vec3f =
|
||||||
|
Vec3f(
|
||||||
|
vec.x * xx + vec.y * xy,
|
||||||
|
vec.x * yx + vec.y * yy,
|
||||||
|
vec.x * zx + vec.y * zy,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,17 @@
|
||||||
package net.liquidev.dawd3.common
|
package net.liquidev.dawd3.common
|
||||||
|
|
||||||
import java.util.concurrent.Executor
|
import java.util.function.Function
|
||||||
|
|
||||||
class TaskQueue : Executor {
|
class TaskQueue<T, R> {
|
||||||
private val tasks = arrayListOf<Runnable>()
|
private val tasks = arrayListOf<Function<T, R>>()
|
||||||
|
|
||||||
override fun execute(task: Runnable) {
|
fun enqueue(task: Function<T, R>) {
|
||||||
tasks.add(task)
|
tasks.add(task)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun flush() {
|
fun flush(argument: T) {
|
||||||
for (task in tasks) {
|
for (task in tasks) {
|
||||||
task.run()
|
task.apply(argument)
|
||||||
}
|
}
|
||||||
tasks.clear()
|
tasks.clear()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,147 @@
|
||||||
package net.liquidev.dawd3.common
|
package net.liquidev.dawd3.common
|
||||||
|
|
||||||
import net.minecraft.util.math.Vec2f
|
import net.minecraft.util.math.*
|
||||||
import net.minecraft.util.math.Vec3f
|
import kotlin.math.PI
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.min
|
import kotlin.math.min
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
operator fun Vec2f.plus(other: Vec2f): Vec2f = Vec2f(x + other.x, y + other.y)
|
operator fun Vec2f.plus(other: Vec2f): Vec2f = Vec2f(x + other.x, y + other.y)
|
||||||
operator fun Vec2f.minus(other: Vec2f): Vec2f = Vec2f(x - other.x, y - other.y)
|
operator fun Vec2f.minus(other: Vec2f): Vec2f = Vec2f(x - other.x, y - other.y)
|
||||||
operator fun Vec2f.times(other: Vec2f): Vec2f = Vec2f(x * other.x, y * other.y)
|
operator fun Vec2f.times(other: Vec2f): Vec2f = Vec2f(x * other.x, y * other.y)
|
||||||
operator fun Vec2f.div(other: Vec2f): Vec2f = Vec2f(x / other.x, y / other.y)
|
operator fun Vec2f.div(other: Vec2f): Vec2f = Vec2f(x / other.x, y / other.y)
|
||||||
|
operator fun Vec2f.div(other: Float): Vec2f = Vec2f(x / other, y / other)
|
||||||
|
|
||||||
|
fun Vec2f.format() = "($x, $y)"
|
||||||
|
|
||||||
|
operator fun Vec3f.plus(other: Vec3f): Vec3f = Vec3f(x + other.x, y + other.y, z + other.z)
|
||||||
|
operator fun Vec3f.minus(other: Vec3f): Vec3f = Vec3f(x - other.x, y - other.y, z - other.z)
|
||||||
|
operator fun Vec3f.times(other: Vec3f): Vec3f = Vec3f(x * other.x, y * other.y, z * other.z)
|
||||||
|
operator fun Vec3f.times(other: Float): Vec3f = Vec3f(x * other, y * other, z * other)
|
||||||
|
operator fun Vec3f.div(other: Vec3f): Vec3f = Vec3f(x / other.x, y / other.y, z / other.z)
|
||||||
|
|
||||||
fun Vec3f.min(other: Vec3f): Vec3f = Vec3f(min(x, other.x), min(y, other.y), min(z, other.z))
|
fun Vec3f.min(other: Vec3f): Vec3f = Vec3f(min(x, other.x), min(y, other.y), min(z, other.z))
|
||||||
fun Vec3f.max(other: Vec3f): Vec3f = Vec3f(max(x, other.x), max(y, other.y), max(z, other.z))
|
fun Vec3f.max(other: Vec3f): Vec3f = Vec3f(max(x, other.x), max(y, other.y), max(z, other.z))
|
||||||
|
|
||||||
|
val Vec3f.lengthSquared get() = x * x + y * y + z * z
|
||||||
|
val Vec3f.length get() = sqrt(lengthSquared)
|
||||||
|
|
||||||
|
fun Vec3f.normalize() {
|
||||||
|
val length = length
|
||||||
|
if (length != 0f) {
|
||||||
|
set(x / length, y / length, z / length)
|
||||||
|
} else {
|
||||||
|
set(0f, 0f, 0f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
operator fun Vec3d.plus(other: Vec3d): Vec3d = Vec3d(x + other.x, y + other.y, z + other.z)
|
||||||
|
operator fun Vec3d.minus(other: Vec3d): Vec3d = Vec3d(x - other.x, y - other.y, z - other.z)
|
||||||
|
operator fun Vec3d.times(other: Vec3d): Vec3d = Vec3d(x * other.x, y * other.y, z * other.z)
|
||||||
|
operator fun Vec3d.div(other: Vec3d): Vec3d = Vec3d(x / other.x, y / other.y, z / other.z)
|
||||||
|
|
||||||
|
operator fun BlockPos.plus(other: BlockPos): BlockPos =
|
||||||
|
BlockPos(x + other.x, y + other.y, z + other.z)
|
||||||
|
|
||||||
|
operator fun BlockPos.minus(other: BlockPos): BlockPos =
|
||||||
|
BlockPos(x - other.x, y - other.y, z - other.z)
|
||||||
|
|
||||||
|
private object PlaneMatrices3Dto2D {
|
||||||
|
val xyPlane = Mat2x3f(1f, 0f, 0f, 0f, 1f, 0f)
|
||||||
|
val zyPlane = Mat2x3f(0f, 0f, 1f, 0f, 1f, 0f)
|
||||||
|
val xzPlane = Mat2x3f(1f, 0f, 0f, 0f, 0f, 1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
val Direction.to2DPlane
|
||||||
|
get() =
|
||||||
|
when (this) {
|
||||||
|
Direction.DOWN -> PlaneMatrices3Dto2D.xzPlane
|
||||||
|
Direction.UP -> PlaneMatrices3Dto2D.xzPlane
|
||||||
|
Direction.NORTH -> PlaneMatrices3Dto2D.xyPlane
|
||||||
|
Direction.SOUTH -> PlaneMatrices3Dto2D.xyPlane
|
||||||
|
Direction.WEST -> PlaneMatrices3Dto2D.zyPlane
|
||||||
|
Direction.EAST -> PlaneMatrices3Dto2D.zyPlane
|
||||||
|
}
|
||||||
|
|
||||||
|
private object CorrectionMatrices {
|
||||||
|
val invertXandY = Affine2x2f(-1f, 0f, 0f, -1f, translateX = 1f, translateY = 1f)
|
||||||
|
val invertYOnly = Affine2x2f(1f, 0f, 0f, -1f, translateX = 0f, translateY = 1f)
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class HorizontalDirection(
|
||||||
|
val index: Int,
|
||||||
|
val x: Int,
|
||||||
|
val z: Int,
|
||||||
|
val angle: Float,
|
||||||
|
val direction: Direction,
|
||||||
|
) {
|
||||||
|
East(0, x = 1, z = 0, angle = 0f, direction = Direction.EAST),
|
||||||
|
South(1, x = 0, z = 1, angle = 0.5f * PI.toFloat(), direction = Direction.SOUTH),
|
||||||
|
West(2, x = -1, z = 0, angle = PI.toFloat(), direction = Direction.WEST),
|
||||||
|
North(3, x = 0, z = -1, angle = 1.5f * PI.toFloat(), direction = Direction.NORTH);
|
||||||
|
|
||||||
|
operator fun plus(other: HorizontalDirection): HorizontalDirection =
|
||||||
|
fromIndex((index + other.index) % values.size)
|
||||||
|
|
||||||
|
operator fun minus(other: HorizontalDirection): HorizontalDirection =
|
||||||
|
fromIndex((index - other.index).mod(values.size))
|
||||||
|
|
||||||
|
fun clockwise() = plus(South)
|
||||||
|
fun counterClockwise() = minus(South)
|
||||||
|
|
||||||
|
fun rotateY(vec: Vec3f): Vec3f =
|
||||||
|
when (this) {
|
||||||
|
East -> vec
|
||||||
|
North -> Vec3f(vec.z, vec.y, -vec.x)
|
||||||
|
West -> Vec3f(-vec.x, vec.y, -vec.z)
|
||||||
|
South -> Vec3f(-vec.z, vec.y, vec.x)
|
||||||
|
}
|
||||||
|
|
||||||
|
val faceCorrection
|
||||||
|
get() =
|
||||||
|
when (this) {
|
||||||
|
East -> CorrectionMatrices.invertXandY
|
||||||
|
South -> CorrectionMatrices.invertYOnly
|
||||||
|
West -> CorrectionMatrices.invertYOnly
|
||||||
|
North -> CorrectionMatrices.invertXandY
|
||||||
|
}
|
||||||
|
|
||||||
|
val vector get() = Vec3i(x, 0, z)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val values = values()
|
||||||
|
|
||||||
|
fun fromIndex(i: Int) = values[i]
|
||||||
|
|
||||||
|
fun fromDirection(direction: Direction): HorizontalDirection? =
|
||||||
|
when (direction) {
|
||||||
|
Direction.EAST -> East
|
||||||
|
Direction.SOUTH -> South
|
||||||
|
Direction.WEST -> West
|
||||||
|
Direction.NORTH -> North
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Vec3d.toVec3f() = Vec3f(x.toFloat(), y.toFloat(), z.toFloat())
|
||||||
|
fun Vec3f.toVec3d() = Vec3d(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
fun Vec3i.toVec3f() = Vec3f(x.toFloat(), y.toFloat(), z.toFloat())
|
||||||
|
fun BlockPos.toVec3d() = Vec3d(x.toDouble(), y.toDouble(), z.toDouble())
|
||||||
|
|
||||||
|
fun pointInRectangle(point: Vec2f, topLeft: Vec2f, bottomRight: Vec2f) =
|
||||||
|
point.x >= topLeft.x && point.y >= topLeft.y &&
|
||||||
|
point.x <= bottomRight.x && point.y <= bottomRight.y
|
||||||
|
|
||||||
|
fun lerp(a: Float, b: Float, t: Float): Float =
|
||||||
|
a + t * (b - a)
|
||||||
|
|
||||||
|
object Cuboids {
|
||||||
|
val fullBlock = Box(0.0, 0.0, 0.0, 1.0, 1.0, 1.0)
|
||||||
|
val halfBlock = Box(0.0, 0.0, 0.0, 1.0, 0.5, 1.0)
|
||||||
|
|
||||||
|
fun newF(from: Vec3f, to: Vec3f) = Box(from.toVec3d(), to.toVec3d())
|
||||||
|
}
|
||||||
|
|
||||||
|
val Box.fromF get() = Vec3f(minX.toFloat(), minY.toFloat(), minZ.toFloat())
|
||||||
|
val Box.toF get() = Vec3f(maxX.toFloat(), maxY.toFloat(), maxZ.toFloat())
|
||||||
|
|
|
||||||
10
src/main/kotlin/net/liquidev/dawd3/common/world.kt
Normal file
10
src/main/kotlin/net/liquidev/dawd3/common/world.kt
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
package net.liquidev.dawd3.common
|
||||||
|
|
||||||
|
import net.minecraft.block.Block
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.world.World
|
||||||
|
|
||||||
|
fun World.syncBlockToClients(position: BlockPos) {
|
||||||
|
val state = getBlockState(position)
|
||||||
|
updateListeners(position, state, state, Block.NOTIFY_LISTENERS)
|
||||||
|
}
|
||||||
|
|
@ -5,6 +5,8 @@ import net.fabricmc.fabric.api.datagen.v1.provider.FabricModelProvider
|
||||||
import net.liquidev.dawd3.Mod
|
import net.liquidev.dawd3.Mod
|
||||||
import net.liquidev.dawd3.block.Blocks
|
import net.liquidev.dawd3.block.Blocks
|
||||||
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
||||||
|
import net.liquidev.dawd3.item.BasicItem
|
||||||
|
import net.liquidev.dawd3.item.Items
|
||||||
import net.minecraft.data.client.*
|
import net.minecraft.data.client.*
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.Identifier
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
@ -19,7 +21,9 @@ class ModelDatagen(generator: FabricDataGenerator) : FabricModelProvider(generat
|
||||||
|
|
||||||
for ((id, deviceBlock) in Blocks.deviceBlocks) {
|
for ((id, deviceBlock) in Blocks.deviceBlocks) {
|
||||||
val modelId = Identifier(Mod.id, "block/${id.path}")
|
val modelId = Identifier(Mod.id, "block/${id.path}")
|
||||||
generator.modelCollector.accept(modelId) { DeviceBlockModel.generate(id, deviceBlock) }
|
generator.modelCollector.accept(modelId) {
|
||||||
|
DeviceBlockModel.generate(deviceBlock)
|
||||||
|
}
|
||||||
generator.blockStateCollector.accept(horizontallyRotatableBlockState(id, deviceBlock))
|
generator.blockStateCollector.accept(horizontallyRotatableBlockState(id, deviceBlock))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -36,14 +40,21 @@ class ModelDatagen(generator: FabricDataGenerator) : FabricModelProvider(generat
|
||||||
.coordinate(BlockStateModelGenerator.createNorthDefaultHorizontalRotationStates())
|
.coordinate(BlockStateModelGenerator.createNorthDefaultHorizontalRotationStates())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
override fun generateItemModels(generator: ItemModelGenerator) {
|
override fun generateItemModels(generator: ItemModelGenerator) {
|
||||||
logger.info("generating item models")
|
logger.info("generating block item models")
|
||||||
for ((id, deviceBlock) in Blocks.deviceBlocks) {
|
for ((id, deviceBlock) in Blocks.deviceBlocks) {
|
||||||
generator.register(
|
generator.register(
|
||||||
deviceBlock.item.item.item,
|
deviceBlock.item.item.item,
|
||||||
Model(Optional.of(Identifier(Mod.id, "block/${id.path}")), Optional.empty())
|
Model(Optional.of(Identifier(Mod.id, "block/${id.path}")), Optional.empty())
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.info("generating other item models")
|
||||||
|
for (registered in Items.registry.registered) {
|
||||||
|
val item = registered.item.item
|
||||||
|
if (item is BasicItem) {
|
||||||
|
generator.register(registered.item.item, Models.GENERATED)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,8 @@ package net.liquidev.dawd3.datagen.device
|
||||||
|
|
||||||
import com.google.gson.JsonObject
|
import com.google.gson.JsonObject
|
||||||
import net.liquidev.dawd3.Mod
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.audio.device.PortDirection
|
||||||
|
import net.liquidev.dawd3.audio.device.PortName
|
||||||
import net.liquidev.dawd3.block.Blocks
|
import net.liquidev.dawd3.block.Blocks
|
||||||
import net.liquidev.dawd3.block.device.PhysicalPort
|
import net.liquidev.dawd3.block.device.PhysicalPort
|
||||||
import net.liquidev.dawd3.common.*
|
import net.liquidev.dawd3.common.*
|
||||||
|
|
@ -11,86 +13,87 @@ import net.liquidev.dawd3.datagen.json.faces
|
||||||
import net.liquidev.dawd3.datagen.json.uvRect
|
import net.liquidev.dawd3.datagen.json.uvRect
|
||||||
import net.liquidev.dawd3.datagen.jsonArray
|
import net.liquidev.dawd3.datagen.jsonArray
|
||||||
import net.liquidev.dawd3.datagen.jsonObject
|
import net.liquidev.dawd3.datagen.jsonObject
|
||||||
import net.minecraft.util.Identifier
|
import net.minecraft.util.math.Box
|
||||||
import net.minecraft.util.math.Vec2f
|
import net.minecraft.util.math.Vec2f
|
||||||
import net.minecraft.util.math.Vec3f
|
import net.minecraft.util.math.Vec3f
|
||||||
|
|
||||||
object DeviceBlockModel {
|
object DeviceBlockModel {
|
||||||
private val portSize = Vec2f(3f, 3f)
|
val portSize = Vec2f(4f, 4f)
|
||||||
private const val portTexture = "${Mod.id}:device/port"
|
private const val portTexture = "${Mod.id}:device/port"
|
||||||
|
|
||||||
private fun getSideTexture(id: Identifier) =
|
fun generate(deviceBlock: Blocks.RegisteredDeviceBlock): JsonObject {
|
||||||
"${Mod.id}:block/${id.path}_side"
|
val faceTextures = deviceBlock.descriptor.faceTextures
|
||||||
|
|
||||||
private fun getFrontTexture(id: Identifier) =
|
|
||||||
"${Mod.id}:block/${id.path}_front"
|
|
||||||
|
|
||||||
fun generate(
|
|
||||||
id: Identifier,
|
|
||||||
deviceBlock: Blocks.RegisteredDeviceBlock,
|
|
||||||
): JsonObject {
|
|
||||||
return jsonObject {
|
return jsonObject {
|
||||||
add("parent", "block/block")
|
add("parent", "block/block")
|
||||||
|
|
||||||
add("textures", jsonObject {
|
add("textures", jsonObject {
|
||||||
add("side", getSideTexture(id))
|
add("front", faceTextures.front.toString())
|
||||||
add("front", getFrontTexture(id))
|
add("back", faceTextures.back.toString())
|
||||||
|
add("right", faceTextures.right.toString())
|
||||||
|
add("left", faceTextures.left.toString())
|
||||||
|
add("top", faceTextures.top.toString())
|
||||||
|
add("bottom", faceTextures.bottom.toString())
|
||||||
|
add("particle", faceTextures.particle.toString())
|
||||||
add("port", portTexture)
|
add("port", portTexture)
|
||||||
add("particle", "#side")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
add("elements", jsonArray {
|
add("elements", jsonArray {
|
||||||
add(blockElement)
|
add(getBlockElement(deviceBlock.descriptor.cuboid))
|
||||||
|
|
||||||
val descriptor = deviceBlock.descriptor
|
val descriptor = deviceBlock.descriptor
|
||||||
for (port in descriptor.portLayout) {
|
for ((portName, port) in descriptor.portLayout) {
|
||||||
add(getPortElement(port))
|
add(getPortElement(portName, port))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private val blockElement = element(
|
private fun getBlockElement(cuboid: Box) = element(
|
||||||
from = Vec3f(0f, 0f, 0f),
|
from = Vec3f(cuboid.minX.toFloat(), cuboid.minY.toFloat(), cuboid.minZ.toFloat()) * 16f,
|
||||||
to = Vec3f(16f, 16f, 16f),
|
to = Vec3f(cuboid.maxX.toFloat(), cuboid.maxY.toFloat(), cuboid.maxZ.toFloat()) * 16f,
|
||||||
faces = faces(
|
faces = faces(
|
||||||
north = face(texture = "#front", cullface = "north"),
|
north = face(texture = "#front", cullface = "north"),
|
||||||
east = face(texture = "#side", cullface = "east"),
|
east = face(texture = "#right", cullface = "east"),
|
||||||
south = face(texture = "#side", cullface = "south"),
|
south = face(texture = "#back", cullface = "south"),
|
||||||
west = face(texture = "#side", cullface = "west"),
|
west = face(texture = "#left", cullface = "west"),
|
||||||
up = face(texture = "#side", cullface = "up"),
|
up = face(texture = "#top", cullface = "up"),
|
||||||
down = face(texture = "#side", cullface = "down"),
|
down = face(texture = "#bottom", cullface = "down"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
private val portPlayArea = Vec2f(16f, 16f) - portSize
|
private val portPlayArea = Vec2f(16f, 16f) - portSize
|
||||||
private fun relativeToAbsolutePortPosition(position: Vec2f): Vec2f {
|
fun relativeToAbsolutePortPosition(position: Vec2f): Vec2f {
|
||||||
val inv = Vec2f(1f - position.x, 1f - position.y)
|
return position * portPlayArea
|
||||||
return inv * portPlayArea
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getPortElement(port: PhysicalPort): JsonObject {
|
private fun getPortElement(portName: PortName, port: PhysicalPort): JsonObject {
|
||||||
val bottomLeft2D = relativeToAbsolutePortPosition(port.position)
|
val bottomLeft2D =
|
||||||
|
relativeToAbsolutePortPosition(Vec2f(1f - port.position.x, 1f - port.position.y))
|
||||||
val topRight2D = bottomLeft2D + portSize
|
val topRight2D = bottomLeft2D + portSize
|
||||||
val bottomLeft = Vec3f(bottomLeft2D.x, bottomLeft2D.y, -0.01f)
|
val bottomLeft = Vec3f(bottomLeft2D.x, bottomLeft2D.y, -0.01f)
|
||||||
val topRight = Vec3f(topRight2D.x, topRight2D.y, 0.01f)
|
val topRight = Vec3f(topRight2D.x, topRight2D.y, 0.01f)
|
||||||
|
|
||||||
val rotatedBottomLeft = port.side.transform.timesXZ(bottomLeft)
|
val rotatedBottomLeft = port.side.modelTransform.timesXZ(bottomLeft)
|
||||||
val rotatedTopRight = port.side.transform.timesXZ(topRight)
|
val rotatedTopRight = port.side.modelTransform.timesXZ(topRight)
|
||||||
|
|
||||||
val min = rotatedBottomLeft.min(rotatedTopRight)
|
val min = rotatedBottomLeft.min(rotatedTopRight)
|
||||||
val max = rotatedBottomLeft.max(rotatedTopRight)
|
val max = rotatedBottomLeft.max(rotatedTopRight)
|
||||||
|
|
||||||
|
val uvX = when (portName.direction) {
|
||||||
|
PortDirection.Input -> 0f
|
||||||
|
PortDirection.Output -> 4f
|
||||||
|
}
|
||||||
|
|
||||||
return element(
|
return element(
|
||||||
from = min,
|
from = min,
|
||||||
to = max,
|
to = max,
|
||||||
faces = jsonObject {
|
faces = jsonObject {
|
||||||
val faceName = port.side.face.getName()
|
val faceName = port.side.face.direction.getName()
|
||||||
add(
|
add(
|
||||||
faceName, face(
|
faceName, face(
|
||||||
texture = "#port",
|
texture = "#port",
|
||||||
cullface = faceName,
|
cullface = faceName,
|
||||||
uv = uvRect(0f, 0f, 4f, 4f)
|
uv = uvRect(uvX, 0f, uvX + 4f, 4f)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
6
src/main/kotlin/net/liquidev/dawd3/item/BasicItem.kt
Normal file
6
src/main/kotlin/net/liquidev/dawd3/item/BasicItem.kt
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package net.liquidev.dawd3.item
|
||||||
|
|
||||||
|
import net.minecraft.item.Item
|
||||||
|
|
||||||
|
/** Parent class for telling apart block items from other items. */
|
||||||
|
abstract class BasicItem(settings: Settings) : Item(settings)
|
||||||
|
|
@ -36,13 +36,32 @@ object Items {
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tools
|
// Tools
|
||||||
val patchCable =
|
private fun coloredPatchCable(name: String, color: Byte) =
|
||||||
addItem("patch_cable", PatchCable(FabricItemSettings().group(ItemGroup.REDSTONE)))
|
addItem(name, PatchCableItem(FabricItemSettings().group(ItemGroup.REDSTONE), color))
|
||||||
|
|
||||||
|
val patchCables = arrayOf(
|
||||||
|
coloredPatchCable("white_patch_cable", color = 0),
|
||||||
|
coloredPatchCable("orange_patch_cable", color = 1),
|
||||||
|
coloredPatchCable("magenta_patch_cable", color = 2),
|
||||||
|
coloredPatchCable("light_blue_patch_cable", color = 3),
|
||||||
|
coloredPatchCable("yellow_patch_cable", color = 4),
|
||||||
|
coloredPatchCable("lime_patch_cable", color = 5),
|
||||||
|
coloredPatchCable("pink_patch_cable", color = 6),
|
||||||
|
coloredPatchCable("gray_patch_cable", color = 7),
|
||||||
|
coloredPatchCable("light_gray_patch_cable", color = 8),
|
||||||
|
coloredPatchCable("cyan_patch_cable", color = 9),
|
||||||
|
coloredPatchCable("purple_patch_cable", color = 10),
|
||||||
|
coloredPatchCable("blue_patch_cable", color = 11),
|
||||||
|
coloredPatchCable("brown_patch_cable", color = 12),
|
||||||
|
coloredPatchCable("green_patch_cable", color = 13),
|
||||||
|
coloredPatchCable("red_patch_cable", color = 14),
|
||||||
|
coloredPatchCable("black_patch_cable", color = 15),
|
||||||
|
)
|
||||||
|
|
||||||
fun addItem(name: Identifier, item: Item): D3Registry.Registered<RegisteredItem> =
|
fun addItem(name: Identifier, item: Item): D3Registry.Registered<RegisteredItem> =
|
||||||
registry.add(name, RegisteredItem(item))
|
registry.add(name, RegisteredItem(item))
|
||||||
|
|
||||||
fun addItem(name: String, item: Item) =
|
private fun addItem(name: String, item: Item) =
|
||||||
addItem(Identifier(Mod.id, name), item)
|
addItem(Identifier(Mod.id, name), item)
|
||||||
|
|
||||||
data class RegisteredItem(
|
data class RegisteredItem(
|
||||||
|
|
|
||||||
|
|
@ -1,16 +0,0 @@
|
||||||
package net.liquidev.dawd3.item
|
|
||||||
|
|
||||||
import net.minecraft.entity.player.PlayerEntity
|
|
||||||
import net.minecraft.item.Item
|
|
||||||
import net.minecraft.item.ItemStack
|
|
||||||
import net.minecraft.sound.SoundEvents
|
|
||||||
import net.minecraft.util.Hand
|
|
||||||
import net.minecraft.util.TypedActionResult
|
|
||||||
import net.minecraft.world.World
|
|
||||||
|
|
||||||
class PatchCable(settings: Settings) : Item(settings) {
|
|
||||||
override fun use(world: World, user: PlayerEntity, hand: Hand): TypedActionResult<ItemStack> {
|
|
||||||
user.playSound(SoundEvents.BLOCK_METAL_PLACE, 1.0f, 1.0f)
|
|
||||||
return TypedActionResult.success(user.getStackInHand(hand))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
141
src/main/kotlin/net/liquidev/dawd3/item/PatchCableItem.kt
Normal file
141
src/main/kotlin/net/liquidev/dawd3/item/PatchCableItem.kt
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
package net.liquidev.dawd3.item
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PlayerLookup
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.audio.device.PortName
|
||||||
|
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||||
|
import net.liquidev.dawd3.block.device.PhysicalPort
|
||||||
|
import net.liquidev.dawd3.common.*
|
||||||
|
import net.liquidev.dawd3.datagen.device.DeviceBlockModel
|
||||||
|
import net.liquidev.dawd3.net.ConnectPorts
|
||||||
|
import net.liquidev.dawd3.net.StartConnectingPorts
|
||||||
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
|
import net.minecraft.item.ItemUsageContext
|
||||||
|
import net.minecraft.server.network.ServerPlayerEntity
|
||||||
|
import net.minecraft.server.world.ServerWorld
|
||||||
|
import net.minecraft.util.ActionResult
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.util.math.Vec2f
|
||||||
|
import net.minecraft.util.math.Vec3d
|
||||||
|
|
||||||
|
class PatchCableItem(settings: Settings, val color: Byte) : BasicItem(settings) {
|
||||||
|
override fun useOnBlock(context: ItemUsageContext): ActionResult {
|
||||||
|
val blockEntity = context.world.getBlockEntity(context.blockPos)
|
||||||
|
if (!context.world.isClient && blockEntity is DeviceBlockEntity) {
|
||||||
|
val side = HorizontalDirection.fromDirection(context.side)
|
||||||
|
if (side != null) {
|
||||||
|
val usePosition =
|
||||||
|
calculateUsePositionOnHorizontalFace(context.hitPos, context.blockPos, side)
|
||||||
|
for ((portName, port) in blockEntity.descriptor.portLayout) {
|
||||||
|
if (isUsedOnPort(port, usePosition)) {
|
||||||
|
useOnPort(context, portName)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ActionResult.success(context.world.isClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun calculateUsePositionOnHorizontalFace(
|
||||||
|
hitPosition: Vec3d,
|
||||||
|
blockPosition: BlockPos,
|
||||||
|
side: HorizontalDirection,
|
||||||
|
): Vec2f {
|
||||||
|
val relativeHitPosition = (hitPosition - blockPosition.toVec3d()).toVec3f()
|
||||||
|
val faceCorrection = side.faceCorrection
|
||||||
|
return faceCorrection * (side.direction.to2DPlane * relativeHitPosition)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isUsedOnPort(
|
||||||
|
port: PhysicalPort,
|
||||||
|
usePosition: Vec2f,
|
||||||
|
): Boolean {
|
||||||
|
val usePositionInPixels = usePosition * Vec2f(16f, 16f)
|
||||||
|
val portTopLeft = DeviceBlockModel.relativeToAbsolutePortPosition(port.position)
|
||||||
|
val portBottomRight = portTopLeft + DeviceBlockModel.portSize
|
||||||
|
return pointInRectangle(usePositionInPixels, portTopLeft, portBottomRight)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun useOnPort(context: ItemUsageContext, portName: PortName) {
|
||||||
|
val player = context.player
|
||||||
|
if (player == null || player !is ServerPlayerEntity) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val ongoingConnection = ongoingConnections[player]
|
||||||
|
if (ongoingConnection == null) {
|
||||||
|
startConnecting(player, OngoingConnection(context.blockPos, portName, color))
|
||||||
|
ServerPlayNetworking.send(
|
||||||
|
player,
|
||||||
|
StartConnectingPorts.id,
|
||||||
|
StartConnectingPorts(context.blockPos, portName.toString(), color).serialize()
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
if (portName.direction != ongoingConnection.portName.direction) {
|
||||||
|
val world = context.world as ServerWorld
|
||||||
|
|
||||||
|
val fromPosition = ongoingConnection.blockPosition
|
||||||
|
val fromPort = ongoingConnection.portName
|
||||||
|
val toPosition = context.blockPos
|
||||||
|
|
||||||
|
val witnesses = PlayerLookup.tracking(world, fromPosition).toHashSet()
|
||||||
|
witnesses.addAll(PlayerLookup.tracking(world, toPosition))
|
||||||
|
for (witness in witnesses) {
|
||||||
|
ServerPlayNetworking.send(
|
||||||
|
witness,
|
||||||
|
ConnectPorts.id,
|
||||||
|
ConnectPorts(
|
||||||
|
fromPosition,
|
||||||
|
fromPort.id.toString(),
|
||||||
|
toPosition,
|
||||||
|
portName.id.toString(),
|
||||||
|
ongoingConnection.color,
|
||||||
|
).serialize()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
val fromBlockEntity = world.getBlockEntity(fromPosition)
|
||||||
|
val toBlockEntity = world.getBlockEntity(toPosition)
|
||||||
|
if (fromBlockEntity !is DeviceBlockEntity || toBlockEntity !is DeviceBlockEntity) {
|
||||||
|
logger.error("from or to-block entity is no longer a device")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
DeviceBlockEntity.connectPhysicalDevices(
|
||||||
|
fromBlockEntity,
|
||||||
|
fromPort,
|
||||||
|
toBlockEntity,
|
||||||
|
portName,
|
||||||
|
color,
|
||||||
|
)
|
||||||
|
|
||||||
|
clearOngoingConnection(player)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class OngoingConnection(
|
||||||
|
val blockPosition: BlockPos,
|
||||||
|
val portName: PortName,
|
||||||
|
val color: Byte,
|
||||||
|
)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var logger = Mod.logger<PatchCableItem>()
|
||||||
|
|
||||||
|
internal val ongoingConnections = hashMapOf<PlayerEntity, OngoingConnection>()
|
||||||
|
|
||||||
|
private fun clearOngoingConnection(player: PlayerEntity) {
|
||||||
|
ongoingConnections.remove(player)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun onBlockDestroyed(blockPosition: BlockPos) {
|
||||||
|
ongoingConnections.values.removeAll { it.blockPosition == blockPosition }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun startConnecting(player: PlayerEntity, connection: OngoingConnection) {
|
||||||
|
ongoingConnections[player] = connection
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
src/main/kotlin/net/liquidev/dawd3/mixin/NonModelTextures.kt
Normal file
27
src/main/kotlin/net/liquidev/dawd3/mixin/NonModelTextures.kt
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package net.liquidev.dawd3.mixin
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.render.Textures
|
||||||
|
import net.minecraft.client.render.TexturedRenderLayers
|
||||||
|
import net.minecraft.client.util.SpriteIdentifier
|
||||||
|
import org.spongepowered.asm.mixin.Mixin
|
||||||
|
import org.spongepowered.asm.mixin.injection.At
|
||||||
|
import org.spongepowered.asm.mixin.injection.Inject
|
||||||
|
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo
|
||||||
|
import java.util.function.Consumer
|
||||||
|
|
||||||
|
@Suppress("UNUSED")
|
||||||
|
@Mixin(TexturedRenderLayers::class)
|
||||||
|
abstract class NonModelTextures {
|
||||||
|
private companion object {
|
||||||
|
@Inject(
|
||||||
|
method = ["addDefaultTextures(Ljava/util/function/Consumer;)V"],
|
||||||
|
at = [At(value = "TAIL")]
|
||||||
|
)
|
||||||
|
@JvmStatic
|
||||||
|
private fun addDefaultTextures(adder: Consumer<SpriteIdentifier>, ci: CallbackInfo) {
|
||||||
|
for (texture in Textures.nonModel) {
|
||||||
|
adder.accept(texture)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
88
src/main/kotlin/net/liquidev/dawd3/net/ConnectPorts.kt
Normal file
88
src/main/kotlin/net/liquidev/dawd3/net/ConnectPorts.kt
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
package net.liquidev.dawd3.net
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.audio.device.PortName
|
||||||
|
import net.liquidev.dawd3.block.device.DeviceBlockEntity
|
||||||
|
import net.minecraft.network.PacketByteBuf
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
import net.minecraft.world.World
|
||||||
|
|
||||||
|
data class ConnectPorts(
|
||||||
|
val fromPosition: BlockPos,
|
||||||
|
val fromPort: String,
|
||||||
|
val toPosition: BlockPos,
|
||||||
|
val toPort: String,
|
||||||
|
val color: Byte,
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private val logger = Mod.logger<ConnectPorts>()
|
||||||
|
|
||||||
|
val id = Identifier(Mod.id, "connect_ports")
|
||||||
|
|
||||||
|
fun registerClientReceiver() {
|
||||||
|
ClientPlayNetworking.registerGlobalReceiver(id) { client, _, buffer, _ ->
|
||||||
|
val packet = deserialize(buffer)
|
||||||
|
client.execute {
|
||||||
|
val world = client.world
|
||||||
|
if (world != null) {
|
||||||
|
packet.handleInWorld(world)
|
||||||
|
} else {
|
||||||
|
logger.warn("packet received without a client world")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserialize(buffer: PacketByteBuf): ConnectPorts {
|
||||||
|
return ConnectPorts(
|
||||||
|
fromPosition = buffer.readBlockPos(),
|
||||||
|
fromPort = buffer.readString(),
|
||||||
|
toPosition = buffer.readBlockPos(),
|
||||||
|
toPort = buffer.readString(),
|
||||||
|
color = buffer.readByte(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun serialize(): PacketByteBuf {
|
||||||
|
val buffer = PacketByteBufs.create()
|
||||||
|
buffer.writeBlockPos(fromPosition)
|
||||||
|
buffer.writeString(fromPort)
|
||||||
|
buffer.writeBlockPos(toPosition)
|
||||||
|
buffer.writeString(toPort)
|
||||||
|
buffer.writeByte(color.toInt()) // bruh
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleInWorld(world: World) {
|
||||||
|
val fromBlockEntity = world.getBlockEntity(fromPosition)
|
||||||
|
val toBlockEntity = world.getBlockEntity(toPosition)
|
||||||
|
if (fromBlockEntity !is DeviceBlockEntity || toBlockEntity !is DeviceBlockEntity) {
|
||||||
|
logger.warn("packet received with block entities not being device block entities")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val fromPortName = PortName.fromString(fromPort)
|
||||||
|
if (fromPortName == null) {
|
||||||
|
logger.warn("port $fromPort does not exist on the from-device")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val toPortName = PortName.fromString(toPort)
|
||||||
|
if (toPortName == null) {
|
||||||
|
logger.warn("port $toPort does not exist on the to-device")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.debug("connecting logical device ports ($fromBlockEntity):($fromPortName) -> ($toBlockEntity):($toPortName)")
|
||||||
|
DeviceBlockEntity.connectLogicalDevices(
|
||||||
|
fromBlockEntity,
|
||||||
|
fromPortName,
|
||||||
|
toBlockEntity,
|
||||||
|
toPortName,
|
||||||
|
color,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/main/kotlin/net/liquidev/dawd3/net/Packets.kt
Normal file
8
src/main/kotlin/net/liquidev/dawd3/net/Packets.kt
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
package net.liquidev.dawd3.net
|
||||||
|
|
||||||
|
object Packets {
|
||||||
|
fun registerClientReceivers() {
|
||||||
|
StartConnectingPorts.registerClientReceiver()
|
||||||
|
ConnectPorts.registerClientReceiver()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
package net.liquidev.dawd3.net
|
||||||
|
|
||||||
|
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking
|
||||||
|
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.liquidev.dawd3.audio.device.PortName
|
||||||
|
import net.liquidev.dawd3.item.PatchCableItem
|
||||||
|
import net.minecraft.entity.player.PlayerEntity
|
||||||
|
import net.minecraft.network.PacketByteBuf
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
import net.minecraft.util.math.BlockPos
|
||||||
|
|
||||||
|
class StartConnectingPorts(val blockPosition: BlockPos, val portName: String, val color: Byte) {
|
||||||
|
companion object {
|
||||||
|
private val logger = Mod.logger<ConnectPorts>()
|
||||||
|
|
||||||
|
val id = Identifier(Mod.id, "start_connecting_ports")
|
||||||
|
|
||||||
|
fun registerClientReceiver() {
|
||||||
|
ClientPlayNetworking.registerGlobalReceiver(id) { client, _, buffer, _ ->
|
||||||
|
val packet = deserialize(buffer)
|
||||||
|
client.execute {
|
||||||
|
val player = client.player
|
||||||
|
if (player != null) {
|
||||||
|
packet.handleForPlayer(player)
|
||||||
|
} else {
|
||||||
|
logger.warn("packet received without a client player")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun deserialize(buffer: PacketByteBuf): StartConnectingPorts {
|
||||||
|
return StartConnectingPorts(
|
||||||
|
blockPosition = buffer.readBlockPos(),
|
||||||
|
portName = buffer.readString(),
|
||||||
|
color = buffer.readByte(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun serialize(): PacketByteBuf {
|
||||||
|
val buffer = PacketByteBufs.create()
|
||||||
|
buffer.writeBlockPos(blockPosition)
|
||||||
|
buffer.writeString(portName)
|
||||||
|
buffer.writeByte(color.toInt())
|
||||||
|
return buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun handleForPlayer(player: PlayerEntity) {
|
||||||
|
val portName = PortName.fromString(portName)
|
||||||
|
if (portName == null) {
|
||||||
|
logger.warn("invalid port name ${this.portName}")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
PatchCableItem.startConnecting(
|
||||||
|
player,
|
||||||
|
PatchCableItem.OngoingConnection(blockPosition, portName, color)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/main/kotlin/net/liquidev/dawd3/render/Textures.kt
Normal file
19
src/main/kotlin/net/liquidev/dawd3/render/Textures.kt
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
package net.liquidev.dawd3.render
|
||||||
|
|
||||||
|
import net.liquidev.dawd3.Mod
|
||||||
|
import net.minecraft.client.util.SpriteIdentifier
|
||||||
|
import net.minecraft.screen.PlayerScreenHandler
|
||||||
|
import net.minecraft.util.Identifier
|
||||||
|
|
||||||
|
object Textures {
|
||||||
|
/**
|
||||||
|
* The set of textures that are not referenced by models but need to be loaded into the
|
||||||
|
* block atlas.
|
||||||
|
*/
|
||||||
|
val nonModel = setOf(
|
||||||
|
SpriteIdentifier(
|
||||||
|
PlayerScreenHandler.BLOCK_ATLAS_TEXTURE,
|
||||||
|
Identifier(Mod.id, "device/cable")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"variants": {
|
||||||
|
"": { "model": "dawd3:device/patch_cable_plug" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,20 @@
|
||||||
{
|
{
|
||||||
"itemGroup.dawd3.main": "dawd³",
|
"itemGroup.dawd3.main": "dawd³",
|
||||||
"item.dawd3.patch_cable": "Patch Cable",
|
"item.dawd3.white_patch_cable": "White Patch Cable",
|
||||||
|
"item.dawd3.orange_patch_cable": "Orange Patch Cable",
|
||||||
|
"item.dawd3.magenta_patch_cable": "Magenta Patch Cable",
|
||||||
|
"item.dawd3.light_blue_patch_cable": "Light Blue Patch Cable",
|
||||||
|
"item.dawd3.yellow_patch_cable": "Yellow Patch Cable",
|
||||||
|
"item.dawd3.lime_patch_cable": "Lime Patch Cable",
|
||||||
|
"item.dawd3.pink_patch_cable": "Pink Patch Cable",
|
||||||
|
"item.dawd3.gray_patch_cable": "Gray Patch Cable",
|
||||||
|
"item.dawd3.light_gray_patch_cable": "Light Gray Patch Cable",
|
||||||
|
"item.dawd3.cyan_patch_cable": "Cyan Patch Cable",
|
||||||
|
"item.dawd3.purple_patch_cable": "Purple Patch Cable",
|
||||||
|
"item.dawd3.brown_patch_cable": "Brown Patch Cable",
|
||||||
|
"item.dawd3.blue_patch_cable": "Blue Patch Cable",
|
||||||
|
"item.dawd3.green_patch_cable": "Green Patch Cable",
|
||||||
|
"item.dawd3.red_patch_cable": "Red Patch Cable",
|
||||||
|
"item.dawd3.black_patch_cable": "Black Patch Cable",
|
||||||
"block.dawd3.speaker": "Speaker"
|
"block.dawd3.speaker": "Speaker"
|
||||||
}
|
}
|
||||||
|
|
@ -1,21 +0,0 @@
|
||||||
{
|
|
||||||
"parent": "block/block",
|
|
||||||
"textures": {
|
|
||||||
"side": "dawd3:block/module_side",
|
|
||||||
"particle": "#side"
|
|
||||||
},
|
|
||||||
"elements": [
|
|
||||||
{
|
|
||||||
"from": [0, 0, 0],
|
|
||||||
"to": [16, 16, 16],
|
|
||||||
"faces": {
|
|
||||||
"down": { "texture": "#side", "cullface": "down" },
|
|
||||||
"up": { "texture": "#side", "cullface": "up" },
|
|
||||||
"north": { "texture": "#front", "cullface": "north" },
|
|
||||||
"east": { "texture": "#side", "cullface": "east" },
|
|
||||||
"south": { "texture": "#side", "cullface": "south" },
|
|
||||||
"west": { "texture": "#side", "cullface": "west" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Reference in a new issue