Merge pull request #123 from Nitwel/miniature-2
Make miniature view standalone
This commit is contained in:
commit
0cf4b932f6
|
@ -29,4 +29,4 @@ nodes/OpenHand/node = SubResource("4")
|
||||||
nodes/OpenHand/position = Vector2(-600, 100)
|
nodes/OpenHand/position = Vector2(-600, 100)
|
||||||
nodes/Trigger/node = SubResource("5")
|
nodes/Trigger/node = SubResource("5")
|
||||||
nodes/Trigger/position = Vector2(-360, 20)
|
nodes/Trigger/position = Vector2(-360, 20)
|
||||||
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
|
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
|
||||||
|
|
|
@ -29,4 +29,4 @@ nodes/OpenHand/node = SubResource("3")
|
||||||
nodes/OpenHand/position = Vector2(-600, 100)
|
nodes/OpenHand/position = Vector2(-600, 100)
|
||||||
nodes/Trigger/node = SubResource("5")
|
nodes/Trigger/node = SubResource("5")
|
||||||
nodes/Trigger/position = Vector2(-360, 40)
|
nodes/Trigger/position = Vector2(-360, 40)
|
||||||
node_connections = [&"output", 0, &"Grip", &"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1"]
|
node_connections = [&"Grip", 0, &"Trigger", &"Grip", 1, &"ClosedHand2", &"Trigger", 0, &"OpenHand", &"Trigger", 1, &"ClosedHand1", &"output", 0, &"Grip"]
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
||||||
meshes/create_shadow_meshes=true
|
meshes/create_shadow_meshes=true
|
||||||
meshes/light_baking=1
|
meshes/light_baking=1
|
||||||
meshes/lightmap_texel_size=0.2
|
meshes/lightmap_texel_size=0.2
|
||||||
|
meshes/force_disable_compression=false
|
||||||
skins/use_named_skins=true
|
skins/use_named_skins=true
|
||||||
animation/import=true
|
animation/import=true
|
||||||
animation/fps=30
|
animation/fps=30
|
||||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
201
app/addons/rdot/Rdot.gd
Normal file
201
app/addons/rdot/Rdot.gd
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
class_name R
|
||||||
|
|
||||||
|
static func state(value: Variant, options: Dictionary={}):
|
||||||
|
if value is Dictionary:
|
||||||
|
return store(value)
|
||||||
|
|
||||||
|
return RdotState.new(value, options)
|
||||||
|
|
||||||
|
static func store(value: Dictionary):
|
||||||
|
return RdotStore.new(value)
|
||||||
|
|
||||||
|
static func computed(computation: Callable, options: Dictionary={}):
|
||||||
|
return RdotComputed.new(computation, options)
|
||||||
|
|
||||||
|
## DIY overloading of
|
||||||
|
## bind(target, prop, value)
|
||||||
|
## bind(target, prop, value, watch_signal)
|
||||||
|
## bind(target, prop, store, key, watch_signal)
|
||||||
|
static func bind(target, prop, value, arg1=null, arg2=null):
|
||||||
|
if value is RdotStore:
|
||||||
|
return _bind_store(target, prop, value, arg1, arg2)
|
||||||
|
|
||||||
|
if value is RdotState or value is RdotComputed:
|
||||||
|
return _bind_state(target, prop, value, arg1)
|
||||||
|
|
||||||
|
assert(false, "Invalid arguments to bind, value must be a RdotState, a RdotComputed or a RdotStore")
|
||||||
|
|
||||||
|
static func _bind_store(target, prop, store: RdotStore, key, watch_signal=null):
|
||||||
|
store._access_property(key)
|
||||||
|
|
||||||
|
return _bind_state(target, prop, store._proxied_value[key], watch_signal)
|
||||||
|
|
||||||
|
static func _bind_state(target, prop, value, watch_signal=null):
|
||||||
|
var graph = RdotGraph.getInstance()
|
||||||
|
|
||||||
|
var watch_c = func(new_value):
|
||||||
|
value.value = new_value
|
||||||
|
|
||||||
|
var c = computed(func(_arg):
|
||||||
|
var oldValue=target.get(prop)
|
||||||
|
|
||||||
|
if oldValue != value.value:
|
||||||
|
target.set(prop, value.value)
|
||||||
|
)
|
||||||
|
|
||||||
|
if watch_signal:
|
||||||
|
watch_signal.connect(watch_c)
|
||||||
|
|
||||||
|
graph.watcher.watch([c])
|
||||||
|
c.do_get()
|
||||||
|
|
||||||
|
return func():
|
||||||
|
graph.watcher.unwatch([c])
|
||||||
|
if watch_signal:
|
||||||
|
watch_signal.disconnect(watch_c)
|
||||||
|
|
||||||
|
static func effect(callback: Callable):
|
||||||
|
var graph = RdotGraph.getInstance()
|
||||||
|
|
||||||
|
var deconstructor := Callable()
|
||||||
|
var c = computed(func(_arg):
|
||||||
|
if !deconstructor.is_null():
|
||||||
|
deconstructor.call()
|
||||||
|
|
||||||
|
var result=callback.call(_arg)
|
||||||
|
|
||||||
|
if result is Callable:
|
||||||
|
deconstructor=result
|
||||||
|
)
|
||||||
|
|
||||||
|
graph.watcher.watch([c])
|
||||||
|
c.do_get()
|
||||||
|
|
||||||
|
return func():
|
||||||
|
if !deconstructor.is_null():
|
||||||
|
deconstructor.call()
|
||||||
|
|
||||||
|
graph.watcher.unwatch([c])
|
||||||
|
|
||||||
|
class RdotState:
|
||||||
|
var node: RdotStateInternal
|
||||||
|
var value = null:
|
||||||
|
get:
|
||||||
|
return do_get()
|
||||||
|
set(value):
|
||||||
|
do_set(value)
|
||||||
|
|
||||||
|
func _init(value: Variant, options: Dictionary={}):
|
||||||
|
var ref = RdotStateInternal.createSignal(value)
|
||||||
|
var node = ref[1]
|
||||||
|
self.node = node
|
||||||
|
node.wrapper = self
|
||||||
|
|
||||||
|
if !options.is_empty():
|
||||||
|
var equals = options.get("equals")
|
||||||
|
|
||||||
|
if !equals.is_null():
|
||||||
|
node.equals = equals
|
||||||
|
|
||||||
|
func do_get():
|
||||||
|
return RdotStateInternal.signalGetFn.call(self.node)
|
||||||
|
|
||||||
|
func do_set(value: Variant):
|
||||||
|
var graph = RdotGraph.getInstance()
|
||||||
|
|
||||||
|
assert(graph.isInNotificationPhase() == false, "Writes to signals not permitted during Watcher callback")
|
||||||
|
|
||||||
|
var ref = self.node
|
||||||
|
|
||||||
|
RdotStateInternal.signalSetFn.call(ref, value)
|
||||||
|
|
||||||
|
class RdotComputed:
|
||||||
|
var node: RdotComputedInternal
|
||||||
|
var value = null:
|
||||||
|
get:
|
||||||
|
return do_get()
|
||||||
|
set(value):
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _init(computation: Callable, options: Dictionary={}):
|
||||||
|
var ref = RdotComputedInternal.createdComputed(computation)
|
||||||
|
var node = ref[1]
|
||||||
|
node.consumerAllowSignalWrites = true
|
||||||
|
self.node = node
|
||||||
|
node.wrapper = self
|
||||||
|
|
||||||
|
if options:
|
||||||
|
var equals = options.get("equals")
|
||||||
|
|
||||||
|
if !equals.is_null():
|
||||||
|
node.equals = equals
|
||||||
|
|
||||||
|
func do_get():
|
||||||
|
return RdotComputedInternal.computedGet(node)
|
||||||
|
|
||||||
|
class Watcher:
|
||||||
|
var node: RdotNode
|
||||||
|
|
||||||
|
func _init(notify: Callable):
|
||||||
|
var node = RdotNode.new()
|
||||||
|
node.wrapper = self
|
||||||
|
node.consumerMarkedDirty = notify
|
||||||
|
node.consumerIsAlwaysLive = true
|
||||||
|
node.consumerAllowSignalWrites = false
|
||||||
|
node.producerNode = []
|
||||||
|
self.node = node
|
||||||
|
|
||||||
|
## signals: Array[RdotState | RComputed]
|
||||||
|
func _assertSignals(signals: Array):
|
||||||
|
for s in signals:
|
||||||
|
assert(s is RdotState or s is RdotComputed, "Watcher expects signals to be RdotState or RdotComputed")
|
||||||
|
|
||||||
|
func watch(signals:=[]):
|
||||||
|
_assertSignals(signals)
|
||||||
|
|
||||||
|
var graph = RdotGraph.getInstance()
|
||||||
|
|
||||||
|
var node = self.node
|
||||||
|
node.dirty = false
|
||||||
|
var prev = graph.setActiveConsumer(node)
|
||||||
|
for s in signals:
|
||||||
|
graph.producerAccessed(s.node)
|
||||||
|
|
||||||
|
graph.setActiveConsumer(prev)
|
||||||
|
|
||||||
|
func unwatch(signals: Array):
|
||||||
|
_assertSignals(signals)
|
||||||
|
|
||||||
|
var graph = RdotGraph.getInstance()
|
||||||
|
|
||||||
|
var node = self.node
|
||||||
|
graph.assertConsumerNode(node)
|
||||||
|
|
||||||
|
var indicesToShift = []
|
||||||
|
for i in range(node.producerNode.size()):
|
||||||
|
if signals.has(node.producerNode[i].wrapper):
|
||||||
|
graph.producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
|
||||||
|
indicesToShift.append(i)
|
||||||
|
|
||||||
|
for idx in indicesToShift:
|
||||||
|
var lastIdx = node.producerNode.size() - 1
|
||||||
|
node.producerNode[idx] = node.producerNode[lastIdx]
|
||||||
|
node.producerIndexOfThis[idx] = node.producerIndexOfThis[lastIdx]
|
||||||
|
|
||||||
|
node.producerNode.pop_back()
|
||||||
|
node.producerIndexOfThis.pop_back()
|
||||||
|
node.nextProducerIndex -= 1
|
||||||
|
|
||||||
|
if idx < node.producerNode.size():
|
||||||
|
var idxConsumer = node.producerNode[idx]
|
||||||
|
var producer = node.producerNode[idx]
|
||||||
|
|
||||||
|
graph.assertProducerNode(producer)
|
||||||
|
|
||||||
|
producer.liveConsumerIndexOfThis[idxConsumer] = idx
|
||||||
|
|
||||||
|
## Returns Array[RComputed]
|
||||||
|
func getPending() -> Array:
|
||||||
|
var node = self.node
|
||||||
|
|
||||||
|
return node.producerNode.filter(func(n): return n.dirty).map(func(n): return n.wrapper)
|
13
app/addons/rdot/array.gd
Normal file
13
app/addons/rdot/array.gd
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
class_name RdotArray
|
||||||
|
|
||||||
|
static func do_set(array: Array, index: int, value):
|
||||||
|
if index >= array.size():
|
||||||
|
array.resize(index + 1)
|
||||||
|
|
||||||
|
array[index] = value
|
||||||
|
|
||||||
|
static func do_get(array: Array, index: int):
|
||||||
|
if index >= array.size():
|
||||||
|
return null
|
||||||
|
|
||||||
|
return array[index]
|
64
app/addons/rdot/computed.gd
Normal file
64
app/addons/rdot/computed.gd
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
extends RdotNode
|
||||||
|
class_name RdotComputedInternal
|
||||||
|
|
||||||
|
enum State {
|
||||||
|
SET = 0,
|
||||||
|
UNSET = 1,
|
||||||
|
COMPUTING = 2,
|
||||||
|
ERRORED = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
var value: Variant = null
|
||||||
|
var state: State = State.UNSET
|
||||||
|
var error = null
|
||||||
|
var computation := Callable()
|
||||||
|
var equal := func(this, a, b): return a == b
|
||||||
|
|
||||||
|
static func computedGet(node: RdotComputedInternal) -> Variant:
|
||||||
|
var graph := RdotGraph.getInstance()
|
||||||
|
|
||||||
|
graph.producerUpdateValueVersion(node)
|
||||||
|
graph.producerAccessed(node)
|
||||||
|
|
||||||
|
assert(node.state != State.ERRORED, "Error in computed.")
|
||||||
|
|
||||||
|
return node.value
|
||||||
|
|
||||||
|
static func createdComputed(computation: Callable):
|
||||||
|
var node = RdotComputedInternal.new()
|
||||||
|
node.computation = computation
|
||||||
|
|
||||||
|
var computed = func():
|
||||||
|
return computedGet(node)
|
||||||
|
|
||||||
|
return [computed, node]
|
||||||
|
|
||||||
|
func producerMustRecompute(node: RdotNode) -> bool:
|
||||||
|
return node.state == State.UNSET or node.state == State.COMPUTING
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
self.dirty = true
|
||||||
|
self.producerRecomputeValue = func(node: RdotNode):
|
||||||
|
assert(node.state != State.COMPUTING, "Detected cycle in computations.")
|
||||||
|
|
||||||
|
var graph := RdotGraph.getInstance()
|
||||||
|
|
||||||
|
var oldState = node.state
|
||||||
|
var oldValue = node.value
|
||||||
|
node.state = State.COMPUTING
|
||||||
|
|
||||||
|
var prevConsumer = graph.consumerBeforeComputation(node)
|
||||||
|
var newValue = node.computation.call(node.wrapper)
|
||||||
|
var oldOk = oldState != State.UNSET&&oldState != State.ERRORED
|
||||||
|
var wasEqual = oldOk&&node.equal.call(node.wrapper, oldValue, newValue)
|
||||||
|
|
||||||
|
graph.consumerAfterComputation(node, prevConsumer)
|
||||||
|
|
||||||
|
if wasEqual:
|
||||||
|
node.value = oldValue
|
||||||
|
node.state = oldState
|
||||||
|
return
|
||||||
|
|
||||||
|
node.value = newValue
|
||||||
|
node.state = State.SET
|
||||||
|
node.version += 1
|
225
app/addons/rdot/graph.gd
Normal file
225
app/addons/rdot/graph.gd
Normal file
|
@ -0,0 +1,225 @@
|
||||||
|
extends RefCounted
|
||||||
|
class_name RdotGraph
|
||||||
|
|
||||||
|
static var instance: RdotGraph = null
|
||||||
|
|
||||||
|
static func getInstance() -> RdotGraph:
|
||||||
|
if instance == null:
|
||||||
|
instance = RdotGraph.new()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
var activeConsumer: RdotNode = null
|
||||||
|
var inNotificationPhase := false
|
||||||
|
|
||||||
|
var epoch := 1
|
||||||
|
|
||||||
|
var postSignalSetFn := Callable()
|
||||||
|
var watcherPending := false
|
||||||
|
|
||||||
|
var watcher = R.Watcher.new(func(_arg):
|
||||||
|
if watcherPending:
|
||||||
|
return
|
||||||
|
|
||||||
|
watcherPending=true
|
||||||
|
var endOfFrame=func():
|
||||||
|
|
||||||
|
watcherPending=false
|
||||||
|
for s in watcher.getPending():
|
||||||
|
s.do_get()
|
||||||
|
|
||||||
|
watcher.watch()
|
||||||
|
|
||||||
|
endOfFrame.call_deferred()
|
||||||
|
)
|
||||||
|
|
||||||
|
func setActiveConsumer(consumer: RdotNode) -> RdotNode:
|
||||||
|
var prev = activeConsumer
|
||||||
|
activeConsumer = consumer
|
||||||
|
return prev
|
||||||
|
|
||||||
|
func getActiveConsumer() -> RdotNode:
|
||||||
|
return activeConsumer
|
||||||
|
|
||||||
|
func isInNotificationPhase() -> bool:
|
||||||
|
return inNotificationPhase
|
||||||
|
|
||||||
|
func producerAccessed(node: RdotNode):
|
||||||
|
assert(inNotificationPhase == false, "Signal read during notification phase")
|
||||||
|
|
||||||
|
if activeConsumer == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
if activeConsumer.consumerOnSignalRead.is_null() == false:
|
||||||
|
activeConsumer.consumerOnSignalRead.call(node)
|
||||||
|
|
||||||
|
var idx = activeConsumer.nextProducerIndex;
|
||||||
|
activeConsumer.nextProducerIndex += 1
|
||||||
|
|
||||||
|
assertConsumerNode(activeConsumer)
|
||||||
|
|
||||||
|
if idx < activeConsumer.producerNode.size()&&activeConsumer.producerNode[idx] != node:
|
||||||
|
if consumerIsLive(activeConsumer):
|
||||||
|
var staleProducer = activeConsumer.producerNode[idx]
|
||||||
|
producerRemoveLiveConsumerAtIndex(staleProducer, activeConsumer.producerIndexOfThis[idx])
|
||||||
|
|
||||||
|
if RdotArray.do_get(activeConsumer.producerNode, idx) != node:
|
||||||
|
RdotArray.do_set(activeConsumer.producerNode, idx, node)
|
||||||
|
RdotArray.do_set(activeConsumer.producerIndexOfThis, idx, producerAddLiveConsumer(node, activeConsumer, idx) if consumerIsLive(activeConsumer) else 0)
|
||||||
|
|
||||||
|
RdotArray.do_set(activeConsumer.producerLastReadVersion, idx, node.version)
|
||||||
|
|
||||||
|
func producerIncrementEpoch():
|
||||||
|
epoch += 1
|
||||||
|
|
||||||
|
func producerUpdateValueVersion(node: RdotNode):
|
||||||
|
if consumerIsLive(node)&&!node.dirty:
|
||||||
|
return
|
||||||
|
|
||||||
|
if !node.dirty&&node.lastCleanEpoch == epoch:
|
||||||
|
return
|
||||||
|
|
||||||
|
if !node.producerMustRecompute(node)&&!consumerPollProducersForChange(node):
|
||||||
|
node.dirty = false;
|
||||||
|
node.lastCleanEpoch = epoch
|
||||||
|
return
|
||||||
|
|
||||||
|
if node.producerRecomputeValue.is_null() == false:
|
||||||
|
node.producerRecomputeValue.call(node)
|
||||||
|
|
||||||
|
node.dirty = false
|
||||||
|
node.lastCleanEpoch = epoch
|
||||||
|
|
||||||
|
func producerNotifyConsumers(node: RdotNode):
|
||||||
|
if node.liveConsumerNode == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
var prev = inNotificationPhase
|
||||||
|
inNotificationPhase = true
|
||||||
|
|
||||||
|
for consumer in node.liveConsumerNode:
|
||||||
|
if !consumer.dirty:
|
||||||
|
consumerMarkDirty(consumer)
|
||||||
|
|
||||||
|
inNotificationPhase = prev
|
||||||
|
|
||||||
|
func producerUpdatesAllowed() -> bool:
|
||||||
|
return activeConsumer == null||activeConsumer.consumerAllowSignalWrites != false
|
||||||
|
|
||||||
|
func consumerMarkDirty(node: RdotNode):
|
||||||
|
node.dirty = true
|
||||||
|
producerNotifyConsumers(node)
|
||||||
|
|
||||||
|
if node.consumerMarkedDirty.is_null() == false:
|
||||||
|
node.consumerMarkedDirty.call(node)
|
||||||
|
|
||||||
|
func consumerBeforeComputation(node: RdotNode) -> RdotNode:
|
||||||
|
if node:
|
||||||
|
node.nextProducerIndex = 0
|
||||||
|
|
||||||
|
return setActiveConsumer(node)
|
||||||
|
|
||||||
|
func consumerAfterComputation(node: RdotNode, prevConsumer: RdotNode):
|
||||||
|
setActiveConsumer(prevConsumer)
|
||||||
|
|
||||||
|
if node == null||node.producerNode == null||node.producerIndexOfThis == null||node.producerLastReadVersion == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
if consumerIsLive(node):
|
||||||
|
for i in range(node.nextProducerIndex, node.producerNode.size()):
|
||||||
|
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
|
||||||
|
|
||||||
|
while node.producerNode.size() > node.nextProducerIndex:
|
||||||
|
node.producerNode.pop_back()
|
||||||
|
node.producerLastReadVersion.pop_back()
|
||||||
|
node.producerIndexOfThis.pop_back()
|
||||||
|
|
||||||
|
func consumerPollProducersForChange(node: RdotNode) -> bool:
|
||||||
|
assertConsumerNode(node)
|
||||||
|
|
||||||
|
for i in range(node.producerNode.size()):
|
||||||
|
var producer = node.producerNode[i]
|
||||||
|
var seenVersion = node.producerLastReadVersion[i]
|
||||||
|
|
||||||
|
if seenVersion != producer.version:
|
||||||
|
return true
|
||||||
|
|
||||||
|
producerUpdateValueVersion(producer)
|
||||||
|
|
||||||
|
if seenVersion != producer.version:
|
||||||
|
return true
|
||||||
|
|
||||||
|
return false
|
||||||
|
|
||||||
|
func consumerDestroy(node: RdotNode):
|
||||||
|
assertConsumerNode(node)
|
||||||
|
|
||||||
|
if consumerIsLive(node):
|
||||||
|
for i in range(node.producerNode.size()):
|
||||||
|
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
|
||||||
|
|
||||||
|
node.producerNode.clear()
|
||||||
|
node.producerLastReadVersion.clear()
|
||||||
|
node.producerIndexOfThis.clear()
|
||||||
|
|
||||||
|
if node.liveConsumerNode:
|
||||||
|
node.liveConsumerNode.clear()
|
||||||
|
node.liveConsumerIndexOfThis.clear()
|
||||||
|
|
||||||
|
static func producerAddLiveConsumer(node: RdotNode, consumer: RdotNode, indexOfThis: int) -> int:
|
||||||
|
assertProducerNode(node)
|
||||||
|
assertConsumerNode(node)
|
||||||
|
|
||||||
|
if node.liveConsumerNode.size() == 0:
|
||||||
|
if node.watched.is_null() == false:
|
||||||
|
node.watched.call(node.wrapper)
|
||||||
|
|
||||||
|
for i in range(node.producerNode.size()):
|
||||||
|
node.producerIndexOfThis[i] = producerAddLiveConsumer(node.producerNode[i], node, i)
|
||||||
|
|
||||||
|
node.liveConsumerIndexOfThis.push_back(indexOfThis)
|
||||||
|
node.liveConsumerNode.push_back(consumer)
|
||||||
|
|
||||||
|
return node.liveConsumerNode.size() - 1
|
||||||
|
|
||||||
|
static func producerRemoveLiveConsumerAtIndex(node: RdotNode, idx: int):
|
||||||
|
assertProducerNode(node)
|
||||||
|
assertConsumerNode(node)
|
||||||
|
|
||||||
|
assert(idx < node.liveConsumerNode.size(), "active consumer index %s is out of bounds of %s consumers)" % [idx, node.liveConsumerNode.size()])
|
||||||
|
|
||||||
|
if node.liveConsumerNode.size() == 1:
|
||||||
|
if node.unwatched.is_null() == false:
|
||||||
|
node.unwatched.call(node.wrapper)
|
||||||
|
|
||||||
|
for i in range(node.producerNode.size()):
|
||||||
|
producerRemoveLiveConsumerAtIndex(node.producerNode[i], node.producerIndexOfThis[i])
|
||||||
|
|
||||||
|
var lastIdx = node.liveConsumerNode.size() - 1
|
||||||
|
node.liveConsumerNode[idx] = node.liveConsumerNode[lastIdx]
|
||||||
|
node.liveConsumerIndexOfThis[idx] = node.liveConsumerIndexOfThis[lastIdx]
|
||||||
|
|
||||||
|
node.liveConsumerNode.pop_back()
|
||||||
|
node.liveConsumerIndexOfThis.pop_back()
|
||||||
|
|
||||||
|
if idx < node.liveConsumerNode.size():
|
||||||
|
var idxProducer = node.liveConsumerIndexOfThis[idx]
|
||||||
|
var consumer = node.liveConsumerNode[idx]
|
||||||
|
assertConsumerNode(consumer)
|
||||||
|
consumer.producerIndexOfThis[idxProducer] = idx
|
||||||
|
|
||||||
|
static func consumerIsLive(node: RdotNode) -> bool:
|
||||||
|
return node.consumerIsAlwaysLive||(node.liveConsumerNode != null&&node.liveConsumerNode.size() > 0)
|
||||||
|
|
||||||
|
static func assertConsumerNode(node: RdotNode):
|
||||||
|
if node.producerNode == null:
|
||||||
|
node.producerNode = []
|
||||||
|
if node.producerIndexOfThis == null:
|
||||||
|
node.producerIndexOfThis = []
|
||||||
|
if node.producerLastReadVersion == null:
|
||||||
|
node.producerLastReadVersion = []
|
||||||
|
|
||||||
|
static func assertProducerNode(node: RdotNode):
|
||||||
|
if node.liveConsumerNode == null:
|
||||||
|
node.liveConsumerNode = []
|
||||||
|
if node.liveConsumerIndexOfThis == null:
|
||||||
|
node.liveConsumerIndexOfThis = []
|
38
app/addons/rdot/node.gd
Normal file
38
app/addons/rdot/node.gd
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
extends RefCounted
|
||||||
|
class_name RdotNode
|
||||||
|
|
||||||
|
var version := 0
|
||||||
|
var lastCleanEpoch := 0
|
||||||
|
var dirty := false
|
||||||
|
|
||||||
|
## Array[RdotNode] | null
|
||||||
|
var producerNode = null
|
||||||
|
|
||||||
|
## Array[int] | null
|
||||||
|
var producerLastReadVersion = null
|
||||||
|
|
||||||
|
## Array[int] | null
|
||||||
|
var producerIndexOfThis = null
|
||||||
|
var nextProducerIndex := 0
|
||||||
|
|
||||||
|
## Array[RdotNode] | null
|
||||||
|
var liveConsumerNode = null
|
||||||
|
|
||||||
|
## Array[int] | null
|
||||||
|
var liveConsumerIndexOfThis = null
|
||||||
|
var consumerAllowSignalWrites := false
|
||||||
|
var consumerIsAlwaysLive := false
|
||||||
|
|
||||||
|
var watched: Callable = Callable()
|
||||||
|
var unwatched: Callable = Callable()
|
||||||
|
var wrapper
|
||||||
|
|
||||||
|
func producerMustRecompute(node: RdotNode) -> bool:
|
||||||
|
return false
|
||||||
|
|
||||||
|
var producerRecomputeValue: Callable = Callable()
|
||||||
|
var consumerMarkedDirty: Callable = Callable()
|
||||||
|
var consumerOnSignalRead: Callable = Callable()
|
||||||
|
|
||||||
|
# func _to_string():
|
||||||
|
# return "RdotNode {\n" + ",\n\t".join(get_property_list().map(func(dict): return dict.name + ": " + str(get(dict.name)))) + "\n}"
|
50
app/addons/rdot/state.gd
Normal file
50
app/addons/rdot/state.gd
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
extends RdotNode
|
||||||
|
class_name RdotStateInternal
|
||||||
|
|
||||||
|
var equal: Callable = func(this, a, b): a == b
|
||||||
|
var value: Variant = null
|
||||||
|
|
||||||
|
static func createSignal(initialValue: Variant):
|
||||||
|
var node = RdotStateInternal.new()
|
||||||
|
node.value = initialValue
|
||||||
|
|
||||||
|
var getter = func():
|
||||||
|
RdotGraph.getInstance().producerAccessed(node)
|
||||||
|
return node.value
|
||||||
|
|
||||||
|
return [getter, node]
|
||||||
|
|
||||||
|
static func setPostSignalSetFn(fn: Callable) -> Callable:
|
||||||
|
var graph := RdotGraph.getInstance()
|
||||||
|
var prev = graph.postSignalSetFn
|
||||||
|
graph.postSignalSetFn = fn
|
||||||
|
return prev
|
||||||
|
|
||||||
|
static func signalGetFn(this: RdotStateInternal):
|
||||||
|
RdotGraph.getInstance().producerAccessed(this)
|
||||||
|
return this.value
|
||||||
|
|
||||||
|
static func signalSetFn(node: RdotStateInternal, newValue: Variant):
|
||||||
|
var graph := RdotGraph.getInstance()
|
||||||
|
|
||||||
|
assert(graph.producerUpdatesAllowed())
|
||||||
|
|
||||||
|
if !node.equal.call(node.wrapper, node.value, newValue):
|
||||||
|
node.value = newValue
|
||||||
|
signalValueChanged(node)
|
||||||
|
|
||||||
|
static func signalUpdateFn(node: RdotStateInternal, updater: Callable):
|
||||||
|
var graph := RdotGraph.getInstance()
|
||||||
|
|
||||||
|
assert(graph.producerUpdatesAllowed())
|
||||||
|
|
||||||
|
signalSetFn(node, updater.call(node.value))
|
||||||
|
|
||||||
|
static func signalValueChanged(node: RdotStateInternal):
|
||||||
|
var graph := RdotGraph.getInstance()
|
||||||
|
|
||||||
|
node.version += 1
|
||||||
|
graph.producerIncrementEpoch()
|
||||||
|
graph.producerNotifyConsumers(node)
|
||||||
|
if !graph.postSignalSetFn.is_null():
|
||||||
|
graph.postSignalSetFn.call()
|
37
app/addons/rdot/store.gd
Normal file
37
app/addons/rdot/store.gd
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
extends Object
|
||||||
|
class_name RdotStore
|
||||||
|
|
||||||
|
var _proxied_value = {}
|
||||||
|
var _property_list = []
|
||||||
|
|
||||||
|
func _init(initial_value: Dictionary={}):
|
||||||
|
_proxied_value = initial_value
|
||||||
|
|
||||||
|
_property_list = _proxied_value.keys().map(func(key):
|
||||||
|
return {
|
||||||
|
"name": key,
|
||||||
|
"type": typeof(_proxied_value[key])
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func _get(property):
|
||||||
|
_access_property(property)
|
||||||
|
|
||||||
|
if _proxied_value[property] is RdotStore:
|
||||||
|
return _proxied_value[property]
|
||||||
|
|
||||||
|
return _proxied_value[property].value
|
||||||
|
|
||||||
|
func _set(property, value):
|
||||||
|
_access_property(property)
|
||||||
|
|
||||||
|
_proxied_value[property].value = value
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
func _access_property(property):
|
||||||
|
if (_proxied_value[property] is R.RdotState) == false:
|
||||||
|
_proxied_value[property] = R.state(_proxied_value[property])
|
||||||
|
|
||||||
|
func _get_property_list():
|
||||||
|
return _property_list
|
|
@ -30,5 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
gltf/naming_version=1
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -30,5 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
gltf/naming_version=1
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
|
@ -30,5 +30,5 @@ animation/trimming=false
|
||||||
animation/remove_immutable_tracks=true
|
animation/remove_immutable_tracks=true
|
||||||
import_script/path=""
|
import_script/path=""
|
||||||
_subresources={}
|
_subresources={}
|
||||||
gltf/naming_version=1
|
gltf/naming_version=0
|
||||||
gltf/embedded_image_handling=1
|
gltf/embedded_image_handling=1
|
||||||
|
|
3
app/assets/models/sky_dome/scene_0.png
Normal file
3
app/assets/models/sky_dome/scene_0.png
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:2941c9ce2676249f899aea64a13012d3b2f52dcf239d5f0c09570f14a4433348
|
||||||
|
size 7954301
|
37
app/assets/models/sky_dome/scene_0.png.import
Normal file
37
app/assets/models/sky_dome/scene_0.png.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://d0vi854404eve"
|
||||||
|
path.s3tc="res://.godot/imported/scene_0.png-9a0770c34bd6836b2bfe1d70dcace53b.s3tc.ctex"
|
||||||
|
path.etc2="res://.godot/imported/scene_0.png-9a0770c34bd6836b2bfe1d70dcace53b.etc2.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/models/sky_dome/scene_0.png"
|
||||||
|
dest_files=["res://.godot/imported/scene_0.png-9a0770c34bd6836b2bfe1d70dcace53b.s3tc.ctex", "res://.godot/imported/scene_0.png-9a0770c34bd6836b2bfe1d70dcace53b.etc2.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=0
|
3
app/assets/models/sky_dome/scene_4.png
Normal file
3
app/assets/models/sky_dome/scene_4.png
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:e93ccbdefa9cb54244f602dd3361a296a51c1cf1b4ca69344408b3f81853c6d7
|
||||||
|
size 7587197
|
37
app/assets/models/sky_dome/scene_4.png.import
Normal file
37
app/assets/models/sky_dome/scene_4.png.import
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ds78ylaicfb3u"
|
||||||
|
path.s3tc="res://.godot/imported/scene_4.png-a9e7d42c59bc553f60827e5915c54b69.s3tc.ctex"
|
||||||
|
path.etc2="res://.godot/imported/scene_4.png-a9e7d42c59bc553f60827e5915c54b69.etc2.ctex"
|
||||||
|
metadata={
|
||||||
|
"imported_formats": ["s3tc_bptc", "etc2_astc"],
|
||||||
|
"vram_texture": true
|
||||||
|
}
|
||||||
|
generator_parameters={}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/models/sky_dome/scene_4.png"
|
||||||
|
dest_files=["res://.godot/imported/scene_4.png-a9e7d42c59bc553f60827e5915c54b69.s3tc.ctex", "res://.godot/imported/scene_4.png-a9e7d42c59bc553f60827e5915c54b69.etc2.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=2
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=true
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=0
|
|
@ -5,6 +5,9 @@ const Entity = preload ("../entity.gd")
|
||||||
@onready var label: Label3D = $Label
|
@onready var label: Label3D = $Label
|
||||||
@onready var collision_shape = $CollisionShape3D
|
@onready var collision_shape = $CollisionShape3D
|
||||||
|
|
||||||
|
var sensor_data = {}
|
||||||
|
var unit = null
|
||||||
|
|
||||||
# Called when the node enters the scene tree for the first time.
|
# Called when the node enters the scene tree for the first time.
|
||||||
func _ready():
|
func _ready():
|
||||||
super()
|
super()
|
||||||
|
@ -17,14 +20,21 @@ func _ready():
|
||||||
)
|
)
|
||||||
|
|
||||||
func set_text(stateInfo):
|
func set_text(stateInfo):
|
||||||
|
if stateInfo == null:
|
||||||
|
return
|
||||||
|
|
||||||
var text = stateInfo["state"]
|
var text = stateInfo["state"]
|
||||||
|
|
||||||
if stateInfo["attributes"]["friendly_name"] != null:
|
if stateInfo["attributes"]["friendly_name"] != null:
|
||||||
text = stateInfo["attributes"]["friendly_name"] + "\n" + text
|
text = stateInfo["attributes"]["friendly_name"] + "\n" + text
|
||||||
|
|
||||||
if stateInfo["attributes"].has("unit_of_measurement")&&stateInfo["attributes"]["unit_of_measurement"] != null:
|
if stateInfo["attributes"].has("unit_of_measurement")&&stateInfo["attributes"]["unit_of_measurement"] != null:
|
||||||
|
unit = stateInfo["attributes"]["unit_of_measurement"]
|
||||||
text += " " + stateInfo["attributes"]["unit_of_measurement"]
|
text += " " + stateInfo["attributes"]["unit_of_measurement"]
|
||||||
|
|
||||||
|
if stateInfo["attributes"].has("device_class"):
|
||||||
|
sensor_data[stateInfo["attributes"]["device_class"]] = stateInfo["state"]
|
||||||
|
|
||||||
label.text = text
|
label.text = text
|
||||||
|
|
||||||
var font = label.get_font()
|
var font = label.get_font()
|
||||||
|
@ -35,3 +45,15 @@ func set_text(stateInfo):
|
||||||
|
|
||||||
collision_shape.shape.size.x = size.x * label.pixel_size * 0.5
|
collision_shape.shape.size.x = size.x * label.pixel_size * 0.5
|
||||||
collision_shape.shape.size.y = size.y * label.pixel_size * 0.25
|
collision_shape.shape.size.y = size.y * label.pixel_size * 0.25
|
||||||
|
|
||||||
|
func get_sensor_data(type: String):
|
||||||
|
if sensor_data.has(type) == false:
|
||||||
|
return null
|
||||||
|
|
||||||
|
return sensor_data[type]
|
||||||
|
|
||||||
|
func get_sensor_unit(type: String):
|
||||||
|
if sensor_data.has(type) == false:
|
||||||
|
return null
|
||||||
|
|
||||||
|
return unit
|
||||||
|
|
|
@ -22,7 +22,7 @@ func _ready():
|
||||||
else:
|
else:
|
||||||
RenderingServer.set_debug_generate_wireframes(true)
|
RenderingServer.set_debug_generate_wireframes(true)
|
||||||
|
|
||||||
update_voice_assistant()
|
create_voice_assistant()
|
||||||
|
|
||||||
controller_left.button_pressed.connect(func(name):
|
controller_left.button_pressed.connect(func(name):
|
||||||
_emit_action(name, true, false)
|
_emit_action(name, true, false)
|
||||||
|
@ -47,7 +47,7 @@ func _ready():
|
||||||
if action.name == "menu_button":
|
if action.name == "menu_button":
|
||||||
toggle_menu()
|
toggle_menu()
|
||||||
elif action.name == "by_button":
|
elif action.name == "by_button":
|
||||||
House.body.mini_view=!House.body.mini_view
|
House.body.mini_view.small.value=!House.body.mini_view.small.value
|
||||||
)
|
)
|
||||||
|
|
||||||
EventSystem.on_focus_in.connect(func(event):
|
EventSystem.on_focus_in.connect(func(event):
|
||||||
|
@ -65,16 +65,20 @@ func _ready():
|
||||||
remove_child(keyboard)
|
remove_child(keyboard)
|
||||||
)
|
)
|
||||||
|
|
||||||
func update_voice_assistant():
|
func create_voice_assistant():
|
||||||
if Store.settings.is_loaded() == false:
|
if Store.settings.is_loaded() == false:
|
||||||
await Store.settings.on_loaded
|
await Store.settings.on_loaded
|
||||||
|
|
||||||
if Store.settings.voice_assistant&&voice_assistant == null:
|
var settings_store = Store.settings.state
|
||||||
voice_assistant = VoiceAssistant.instantiate()
|
|
||||||
add_child(voice_assistant)
|
R.effect(func(_arg):
|
||||||
elif !Store.settings.voice_assistant&&voice_assistant != null:
|
if settings_store.voice_assistant == true&&voice_assistant == null:
|
||||||
remove_child(voice_assistant)
|
voice_assistant=VoiceAssistant.instantiate()
|
||||||
voice_assistant.queue_free()
|
add_child(voice_assistant)
|
||||||
|
elif settings_store.voice_assistant == false&&voice_assistant != null:
|
||||||
|
remove_child(voice_assistant)
|
||||||
|
voice_assistant.queue_free()
|
||||||
|
)
|
||||||
|
|
||||||
func toggle_menu():
|
func toggle_menu():
|
||||||
if menu.show_menu == false:
|
if menu.show_menu == false:
|
||||||
|
|
|
@ -48,8 +48,8 @@ func get_new_transform():
|
||||||
return marker.global_transform
|
return marker.global_transform
|
||||||
|
|
||||||
func update_align_reference():
|
func update_align_reference():
|
||||||
corner1.global_position = Store.house.align_position1
|
corner1.global_position = Store.house.state.align_position1
|
||||||
corner2.global_position = Store.house.align_position2
|
corner2.global_position = Store.house.state.align_position2
|
||||||
|
|
||||||
corner2.look_at(corner1.global_position, Vector3.UP)
|
corner2.look_at(corner1.global_position, Vector3.UP)
|
||||||
corner2.rotate(Vector3.UP, deg_to_rad( - 90))
|
corner2.rotate(Vector3.UP, deg_to_rad( - 90))
|
||||||
|
@ -57,5 +57,5 @@ func update_align_reference():
|
||||||
edge.align_to_corners(corner1.global_position, corner2.global_position)
|
edge.align_to_corners(corner1.global_position, corner2.global_position)
|
||||||
|
|
||||||
func update_store():
|
func update_store():
|
||||||
Store.house.align_position1 = corner1.global_position
|
Store.house.state.align_position1 = corner1.global_position
|
||||||
Store.house.align_position2 = corner2.global_position
|
Store.house.state.align_position2 = corner2.global_position
|
|
@ -2,17 +2,16 @@ extends Node3D
|
||||||
|
|
||||||
const Room = preload ("./room/room.tscn")
|
const Room = preload ("./room/room.tscn")
|
||||||
const RoomType = preload ("./room/room.gd")
|
const RoomType = preload ("./room/room.gd")
|
||||||
|
const Miniature = preload ("./mini/miniature.gd")
|
||||||
|
const AlignReference = preload ("./align_reference.gd")
|
||||||
|
|
||||||
@onready var levels = $Levels
|
@onready var levels = $Levels
|
||||||
@onready var collision_shape = $Levels/CollisionShape3D
|
@onready var collision_shape = $Levels/CollisionShape3D
|
||||||
@onready var align_reference = $AlignReference
|
@onready var align_reference: AlignReference = $AlignReference
|
||||||
|
@onready var mini_view: Miniature = $Levels/Miniature
|
||||||
|
|
||||||
var fixing_reference: bool = false
|
var fixing_reference: bool = false
|
||||||
var editing_room: RoomType = null
|
var editing_room: RoomType = null
|
||||||
var mini_view: bool = false:
|
|
||||||
set(value):
|
|
||||||
mini_view = value
|
|
||||||
update_mini_view()
|
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
Store.house.on_loaded.connect(func():
|
Store.house.on_loaded.connect(func():
|
||||||
|
@ -26,18 +25,18 @@ func update_house():
|
||||||
|
|
||||||
align_reference.update_align_reference()
|
align_reference.update_align_reference()
|
||||||
|
|
||||||
for index in range(Store.house.rooms.size() - 1, -1, -1):
|
for index in range(Store.house.state.rooms.size()):
|
||||||
var new_room = Store.house.rooms[index]
|
var new_room = Store.house.state.rooms[index]
|
||||||
|
|
||||||
if new_room.corners.size() == 0:
|
if new_room.corners.size() == 0:
|
||||||
Store.house.rooms.remove_at(index)
|
Store.house.state.rooms.remove_at(index)
|
||||||
Store.house.save_local()
|
Store.house.save_local()
|
||||||
continue
|
continue
|
||||||
|
|
||||||
create_room(new_room.name, 0)
|
create_room(new_room.name, 0)
|
||||||
|
|
||||||
for entity_index in range(Store.house.entities.size()):
|
for entity_index in range(Store.house.state.entities.size()):
|
||||||
var entity = Store.house.entities[entity_index]
|
var entity = Store.house.state.entities[entity_index]
|
||||||
|
|
||||||
var entity_instance = create_entity_in(entity.id, entity.room)
|
var entity_instance = create_entity_in(entity.id, entity.room)
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ func create_room(room_name: String, level: int) -> RoomType:
|
||||||
var existing_room = Store.house.get_room(room_name)
|
var existing_room = Store.house.get_room(room_name)
|
||||||
|
|
||||||
if existing_room == null:
|
if existing_room == null:
|
||||||
Store.house.rooms.append({
|
Store.house.state.rooms.append({
|
||||||
"name": room_name,
|
"name": room_name,
|
||||||
"height": 2.0,
|
"height": 2.0,
|
||||||
"corners": [],
|
"corners": [],
|
||||||
|
@ -101,7 +100,7 @@ func delete_room(room_name):
|
||||||
var store_room = Store.house.get_room(room_name)
|
var store_room = Store.house.get_room(room_name)
|
||||||
|
|
||||||
if store_room != null:
|
if store_room != null:
|
||||||
Store.house.rooms.erase(store_room)
|
Store.house.state.rooms.erase(store_room)
|
||||||
|
|
||||||
Store.house.save_local()
|
Store.house.save_local()
|
||||||
|
|
||||||
|
@ -198,44 +197,6 @@ func create_entity_in(entity_id: String, room_name: String):
|
||||||
|
|
||||||
return entity
|
return entity
|
||||||
|
|
||||||
func update_mini_view():
|
|
||||||
collision_shape.disabled = !mini_view
|
|
||||||
|
|
||||||
var tween = create_tween()
|
|
||||||
tween.set_parallel(true)
|
|
||||||
tween.set_trans(Tween.TRANS_CUBIC)
|
|
||||||
|
|
||||||
if mini_view:
|
|
||||||
var aabb = get_level_aabb(0)
|
|
||||||
aabb.position.y = -0.03
|
|
||||||
aabb.size.y = 0.06
|
|
||||||
var center = aabb.position + aabb.size / 2.0
|
|
||||||
|
|
||||||
collision_shape.global_position = center
|
|
||||||
collision_shape.shape.size = aabb.size
|
|
||||||
|
|
||||||
var camera = get_node("/root/Main/XROrigin3D/XRCamera3D")
|
|
||||||
var camera_position = camera.global_position
|
|
||||||
var camera_direction = -camera.global_transform.basis.z
|
|
||||||
|
|
||||||
camera_position.y *= 0.5
|
|
||||||
camera_direction.y = 0.0
|
|
||||||
|
|
||||||
var target_position = camera_position + camera_direction.normalized() * 0.2
|
|
||||||
var new_position = target_position - center * 0.1
|
|
||||||
tween.tween_property(levels, "global_position", new_position, 0.5)
|
|
||||||
tween.tween_property(levels, "scale", Vector3(0.1, 0.1, 0.1), 0.5)
|
|
||||||
|
|
||||||
for room in get_rooms(0):
|
|
||||||
room.state_machine.change_to("Mini")
|
|
||||||
else:
|
|
||||||
tween.tween_property(levels, "global_position", Vector3(0, 0, 0), 0.5)
|
|
||||||
tween.tween_property(levels, "scale", Vector3(1.0, 1.0, 1.0), 0.5)
|
|
||||||
await tween.finished
|
|
||||||
|
|
||||||
for room in get_rooms(0):
|
|
||||||
room.state_machine.change_to("View")
|
|
||||||
|
|
||||||
func edit_reference():
|
func edit_reference():
|
||||||
fixing_reference = false
|
fixing_reference = false
|
||||||
align_reference.visible = true
|
align_reference.visible = true
|
||||||
|
@ -270,7 +231,7 @@ func save_reference():
|
||||||
Store.house.save_local()
|
Store.house.save_local()
|
||||||
|
|
||||||
func save_all_entities():
|
func save_all_entities():
|
||||||
Store.house.entities.clear()
|
Store.house.state.entities.clear()
|
||||||
|
|
||||||
for room in get_rooms(0):
|
for room in get_rooms(0):
|
||||||
for entity in room.get_node("Entities").get_children():
|
for entity in room.get_node("Entities").get_children():
|
||||||
|
@ -281,6 +242,6 @@ func save_all_entities():
|
||||||
"room": String(room.name)
|
"room": String(room.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
Store.house.entities.append(entity_data)
|
Store.house.state.entities.append(entity_data)
|
||||||
|
|
||||||
Store.house.save_local()
|
Store.house.save_local()
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
[gd_scene load_steps=5 format=3 uid="uid://cbemihbxkd4ll"]
|
[gd_scene load_steps=6 format=3 uid="uid://cbemihbxkd4ll"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://content/system/house/house.gd" id="1_p8amj"]
|
[ext_resource type="Script" path="res://content/system/house/house.gd" id="1_p8amj"]
|
||||||
[ext_resource type="Script" path="res://content/functions/movable.gd" id="2_w1auk"]
|
[ext_resource type="Script" path="res://content/functions/movable.gd" id="2_w1auk"]
|
||||||
[ext_resource type="PackedScene" uid="uid://jls16btb8nko" path="res://content/system/house/align_reference.tscn" id="3_e1tcn"]
|
[ext_resource type="PackedScene" uid="uid://jls16btb8nko" path="res://content/system/house/align_reference.tscn" id="3_e1tcn"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://ds60i5n211hi3" path="res://content/system/house/mini/miniature.tscn" id="4_qjbly"]
|
||||||
|
|
||||||
[sub_resource type="BoxShape3D" id="BoxShape3D_x81up"]
|
[sub_resource type="BoxShape3D" id="BoxShape3D_x81up"]
|
||||||
|
|
||||||
|
@ -22,6 +23,8 @@ script = ExtResource("2_w1auk")
|
||||||
restricted = true
|
restricted = true
|
||||||
lock_rotation = true
|
lock_rotation = true
|
||||||
|
|
||||||
|
[node name="Miniature" parent="Levels" instance=ExtResource("4_qjbly")]
|
||||||
|
|
||||||
[node name="AlignReference" parent="." instance=ExtResource("3_e1tcn")]
|
[node name="AlignReference" parent="." instance=ExtResource("3_e1tcn")]
|
||||||
visible = false
|
visible = false
|
||||||
disabled = true
|
disabled = true
|
||||||
|
|
11
app/content/system/house/mini/humid_gradient.tres
Normal file
11
app/content/system/house/mini/humid_gradient.tres
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[gd_resource type="GradientTexture1D" load_steps=2 format=3 uid="uid://gfemvrlwowg7"]
|
||||||
|
|
||||||
|
[sub_resource type="Gradient" id="Gradient_2jwis"]
|
||||||
|
interpolation_color_space = 2
|
||||||
|
offsets = PackedFloat32Array(0, 0.333333, 0.5, 1)
|
||||||
|
colors = PackedColorArray(0.999396, 0, 0.058647, 1, 1, 0.85, 0, 1, 0, 0.95, 1, 1, 0.0333333, 0, 1, 1)
|
||||||
|
metadata/_snap_enabled = true
|
||||||
|
metadata/_snap_count = 6
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
gradient = SubResource("Gradient_2jwis")
|
3
app/content/system/house/mini/mini_view_options.gd
Normal file
3
app/content/system/house/mini/mini_view_options.gd
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
const mini_wall_shader: ShaderMaterial = preload ("./mini_wall.tres")
|
12
app/content/system/house/mini/mini_wall.tres
Normal file
12
app/content/system/house/mini/mini_wall.tres
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
[gd_resource type="ShaderMaterial" load_steps=3 format=3 uid="uid://bcfcough6ucvc"]
|
||||||
|
|
||||||
|
[ext_resource type="Shader" path="res://content/system/house/mini/mini_wall_shader.gdshader" id="1_sbr3e"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://bbuq4wn7e5o2q" path="res://content/system/house/mini/temp_gradient.tres" id="2_3lwi8"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
render_priority = 0
|
||||||
|
shader = ExtResource("1_sbr3e")
|
||||||
|
shader_parameter/data = PackedFloat32Array()
|
||||||
|
shader_parameter/data_size = 0
|
||||||
|
shader_parameter/alpha = 0.3
|
||||||
|
shader_parameter/color_gradient = ExtResource("2_3lwi8")
|
63
app/content/system/house/mini/mini_wall_shader.gdshader
Normal file
63
app/content/system/house/mini/mini_wall_shader.gdshader
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
shader_type spatial;
|
||||||
|
render_mode blend_mix, depth_draw_opaque, cull_disabled, diffuse_lambert, specular_schlick_ggx, unshaded;
|
||||||
|
|
||||||
|
uniform vec4 data[100];
|
||||||
|
uniform int data_size: hint_range(0, 100, 1);
|
||||||
|
uniform float alpha: hint_range(0.0, 1.0, 0.1) = 0.3;
|
||||||
|
uniform sampler2D color_gradient;
|
||||||
|
|
||||||
|
varying vec3 color;
|
||||||
|
|
||||||
|
float simple_weight(int index, vec3 world_pos, float p) {
|
||||||
|
return 1.0 / pow(distance(data[index].xyz, world_pos), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
float sphere_weight(int index, vec3 world_pos, float r) {
|
||||||
|
float dist = distance(data[index].xyz, world_pos);
|
||||||
|
return pow(max(0, r - dist) / (r * dist), 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
void vertex() {
|
||||||
|
color = vec3(1.0, 1.0, 1.0);
|
||||||
|
|
||||||
|
if(data_size > 0) {
|
||||||
|
float distances[100];
|
||||||
|
float dist_sum = 0.0;
|
||||||
|
float data_sum = 0.0;
|
||||||
|
|
||||||
|
float closest_dist = -1.0;
|
||||||
|
int closest_index = 0;
|
||||||
|
|
||||||
|
// Calculate Global Coordinates
|
||||||
|
//vec3 world_position = (MODEL_MATRIX * vec4(VERTEX, 1.0)).xyz;
|
||||||
|
|
||||||
|
closest_dist = distance(data[0].xyz, VERTEX);
|
||||||
|
|
||||||
|
// Inverse distance weighting using Shepard's method
|
||||||
|
for(int i = 0; i < data_size; i++) {
|
||||||
|
distances[i] = sphere_weight(i, VERTEX, 5.0);
|
||||||
|
dist_sum += distances[i];
|
||||||
|
data_sum += distances[i] * data[i].w;
|
||||||
|
|
||||||
|
float dist = distance(data[i].xyz, VERTEX);
|
||||||
|
|
||||||
|
if(dist < closest_dist) {
|
||||||
|
closest_dist = dist;
|
||||||
|
closest_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float value = (1.0 / dist_sum) * data_sum;
|
||||||
|
|
||||||
|
if( value > 0.0 || value < 1.0) {
|
||||||
|
color.xyz = texture(color_gradient, vec2(value, 0)).xyz;
|
||||||
|
} else {
|
||||||
|
color.xyz = texture(color_gradient, vec2(data[closest_index].w, 0)).xyz;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fragment() {
|
||||||
|
ALBEDO = vec3(color.xyz);
|
||||||
|
ALPHA = alpha;
|
||||||
|
}
|
188
app/content/system/house/mini/miniature.gd
Normal file
188
app/content/system/house/mini/miniature.gd
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
extends Node3D
|
||||||
|
|
||||||
|
const ConstructRoomMesh = preload ("res://lib/utils/mesh/construct_room_mesh.gd")
|
||||||
|
const wall_material = preload ("./mini_wall.tres")
|
||||||
|
|
||||||
|
const humidity_gradient = preload ("./humid_gradient.tres")
|
||||||
|
const temperature_gradient = preload ("./temp_gradient.tres")
|
||||||
|
|
||||||
|
@onready var body = $Body
|
||||||
|
@onready var model = $Body/Model
|
||||||
|
@onready var collision_shape = $Body/CollisionShape3D
|
||||||
|
@onready var toggle_heatmap = $Body/HeatmapButton
|
||||||
|
|
||||||
|
enum HeatmapType {
|
||||||
|
NONE = 0,
|
||||||
|
TEMPERATURE = 1,
|
||||||
|
HUMIDITY = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
var heatmap_type_strings = {
|
||||||
|
HeatmapType.NONE: "none",
|
||||||
|
HeatmapType.TEMPERATURE: "temperature",
|
||||||
|
HeatmapType.HUMIDITY: "humidity"
|
||||||
|
}
|
||||||
|
|
||||||
|
var base_scale = {
|
||||||
|
HeatmapType.NONE: Vector2(0.0, 1.0),
|
||||||
|
HeatmapType.TEMPERATURE: Vector2( - 20.0, 60.0),
|
||||||
|
HeatmapType.HUMIDITY: Vector2(0.0, 100.0)
|
||||||
|
}
|
||||||
|
var selected_scale = R.state(Vector2(0.0, 1.0))
|
||||||
|
var opacity = R.state(30)
|
||||||
|
var heatmap_type = R.state(HeatmapType.NONE)
|
||||||
|
var small = R.state(false)
|
||||||
|
|
||||||
|
func _ready():
|
||||||
|
wall_material.set_shader_parameter("data", [])
|
||||||
|
wall_material.set_shader_parameter("data_size", 0)
|
||||||
|
|
||||||
|
if Store.house.is_loaded() == false:
|
||||||
|
await Store.house.on_loaded
|
||||||
|
|
||||||
|
# Update Room Mesh
|
||||||
|
R.effect(func(_arg):
|
||||||
|
if Store.house.state.rooms.size() == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
if heatmap_type.value == HeatmapType.NONE&&small.value == false:
|
||||||
|
return
|
||||||
|
|
||||||
|
for child in model.get_children():
|
||||||
|
model.remove_child(child)
|
||||||
|
child.free()
|
||||||
|
|
||||||
|
for room in Store.house.state.rooms:
|
||||||
|
var walls_mesh=MeshInstance3D.new()
|
||||||
|
var floor_mesh=MeshInstance3D.new()
|
||||||
|
|
||||||
|
model.add_child(walls_mesh)
|
||||||
|
model.add_child(floor_mesh)
|
||||||
|
|
||||||
|
walls_mesh.mesh=ConstructRoomMesh.generate_wall_mesh_grid(room.corners, room.height)
|
||||||
|
floor_mesh.mesh=ConstructRoomMesh.generate_ceiling_mesh_grid(room.corners)
|
||||||
|
|
||||||
|
walls_mesh.material_override=wall_material
|
||||||
|
floor_mesh.material_override=wall_material
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update Size
|
||||||
|
R.effect(func(_arg):
|
||||||
|
collision_shape.disabled=small.value == false
|
||||||
|
|
||||||
|
var tween:=create_tween()
|
||||||
|
tween.set_parallel(true)
|
||||||
|
if small.value:
|
||||||
|
|
||||||
|
var aabb=House.body.get_level_aabb(0)
|
||||||
|
aabb.position.y=- 0.03
|
||||||
|
aabb.size.y=0.06
|
||||||
|
|
||||||
|
var center=aabb.position + aabb.size / 2
|
||||||
|
|
||||||
|
collision_shape.shape.size=aabb.size * 0.1
|
||||||
|
|
||||||
|
var camera=$"/root/Main/XROrigin3D/XRCamera3D"
|
||||||
|
var camera_position=camera.global_position
|
||||||
|
var camera_direction=- camera.global_transform.basis.z
|
||||||
|
|
||||||
|
camera_position.y *= 0.5
|
||||||
|
camera_direction.y=0
|
||||||
|
|
||||||
|
var target_position=camera_position + camera_direction.normalized() * 0.2
|
||||||
|
var new_position=target_position - center * 0.1
|
||||||
|
|
||||||
|
tween.tween_property(model, "scale", Vector3(0.1, 0.1, 0.1), 0.5)
|
||||||
|
tween.tween_property(body, "position", new_position, 0.5)
|
||||||
|
else:
|
||||||
|
tween.tween_property(model, "scale", Vector3.ONE, 0.5)
|
||||||
|
tween.tween_property(body, "position", Vector3.ZERO, 0.5)
|
||||||
|
tween.tween_property(body, "quaternion", Quaternion.IDENTITY, 0.5)
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update Walls
|
||||||
|
R.effect(func(_arg):
|
||||||
|
var show_map=heatmap_type.value != HeatmapType.NONE
|
||||||
|
var show_small=small.value
|
||||||
|
|
||||||
|
for child in model.get_children():
|
||||||
|
child.visible=show_map||show_small
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update Heatmap
|
||||||
|
R.effect(func(_arg):
|
||||||
|
if heatmap_type.value != HeatmapType.NONE:
|
||||||
|
EventSystem.on_slow_tick.connect(update_data)
|
||||||
|
if heatmap_type.value == HeatmapType.TEMPERATURE:
|
||||||
|
wall_material.set_shader_parameter("color_gradient", temperature_gradient)
|
||||||
|
else:
|
||||||
|
wall_material.set_shader_parameter("color_gradient", humidity_gradient)
|
||||||
|
else:
|
||||||
|
EventSystem.on_slow_tick.disconnect(update_data)
|
||||||
|
wall_material.set_shader_parameter("data", [])
|
||||||
|
wall_material.set_shader_parameter("data_size", 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
R.effect(func(_arg):
|
||||||
|
wall_material.set_shader_parameter("alpha", opacity.value / 100.0)
|
||||||
|
)
|
||||||
|
|
||||||
|
const SensorEntity = preload ("res://content/entities/sensor/sensor.gd")
|
||||||
|
|
||||||
|
func get_base_scale() -> Vector2:
|
||||||
|
return base_scale[heatmap_type.value]
|
||||||
|
|
||||||
|
func get_sensor_data():
|
||||||
|
var data_list = []
|
||||||
|
|
||||||
|
for room in House.body.get_rooms(0):
|
||||||
|
for entity in room.get_node("Entities").get_children():
|
||||||
|
if entity is SensorEntity:
|
||||||
|
var sensor = entity as SensorEntity
|
||||||
|
var data = sensor.get_sensor_data(heatmap_type_strings[heatmap_type.value])
|
||||||
|
if data == null:
|
||||||
|
continue
|
||||||
|
|
||||||
|
var sensor_pos = House.body.to_local(sensor.global_position)
|
||||||
|
|
||||||
|
data_list.append(Vector4(sensor_pos.x, sensor_pos.y, sensor_pos.z, float(data)))
|
||||||
|
|
||||||
|
return data_list
|
||||||
|
|
||||||
|
func get_sensor_unit():
|
||||||
|
for room in House.body.get_rooms(0):
|
||||||
|
for entity in room.get_node("Entities").get_children():
|
||||||
|
if entity is SensorEntity:
|
||||||
|
var sensor = entity as SensorEntity
|
||||||
|
var sensor_unit = sensor.get_sensor_unit(heatmap_type_strings[heatmap_type.value])
|
||||||
|
if sensor_unit != null:
|
||||||
|
return sensor_unit
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
|
func get_sensor_scale():
|
||||||
|
var data = get_sensor_data()
|
||||||
|
var minmax
|
||||||
|
|
||||||
|
for sensor in data:
|
||||||
|
if minmax == null:
|
||||||
|
minmax = Vector2(sensor.w, sensor.w)
|
||||||
|
else:
|
||||||
|
minmax.x = min(sensor.w, minmax.x)
|
||||||
|
minmax.y = max(sensor.w, minmax.y)
|
||||||
|
|
||||||
|
if minmax == null:
|
||||||
|
return Vector2(0.0, 1.0)
|
||||||
|
|
||||||
|
return minmax
|
||||||
|
|
||||||
|
func update_data(_delta: float) -> void:
|
||||||
|
var data_list = get_sensor_data()
|
||||||
|
|
||||||
|
data_list = data_list.map(func(data: Vector4) -> Vector4:
|
||||||
|
data.w=clamp((data.w - selected_scale.value.x) / (selected_scale.value.y - selected_scale.value.x), 0.0, 1.0)
|
||||||
|
return data
|
||||||
|
)
|
||||||
|
|
||||||
|
wall_material.set_shader_parameter("data", data_list)
|
||||||
|
wall_material.set_shader_parameter("data_size", data_list.size())
|
19
app/content/system/house/mini/miniature.tscn
Normal file
19
app/content/system/house/mini/miniature.tscn
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
[gd_scene load_steps=4 format=3 uid="uid://ds60i5n211hi3"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://content/system/house/mini/miniature.gd" id="1_b53yn"]
|
||||||
|
[ext_resource type="Script" path="res://content/functions/movable.gd" id="2_x7oed"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_bckw3"]
|
||||||
|
|
||||||
|
[node name="Miniature" type="Node3D"]
|
||||||
|
script = ExtResource("1_b53yn")
|
||||||
|
|
||||||
|
[node name="Body" type="StaticBody3D" parent="."]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="Body"]
|
||||||
|
shape = SubResource("BoxShape3D_bckw3")
|
||||||
|
|
||||||
|
[node name="Movable" type="Node" parent="Body"]
|
||||||
|
script = ExtResource("2_x7oed")
|
||||||
|
|
||||||
|
[node name="Model" type="Node3D" parent="Body"]
|
11
app/content/system/house/mini/temp_gradient.tres
Normal file
11
app/content/system/house/mini/temp_gradient.tres
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
[gd_resource type="GradientTexture1D" load_steps=2 format=3 uid="uid://bbuq4wn7e5o2q"]
|
||||||
|
|
||||||
|
[sub_resource type="Gradient" id="Gradient_2jwis"]
|
||||||
|
interpolation_color_space = 2
|
||||||
|
offsets = PackedFloat32Array(0.00840336, 0.1, 0.4375, 0.5625, 1)
|
||||||
|
colors = PackedColorArray(0.533333, 0, 1, 1, 0, 0.45, 1, 1, 0, 0.95, 1, 1, 0.983333, 1, 0, 1, 0.999396, 0, 0.058647, 1)
|
||||||
|
metadata/_snap_enabled = true
|
||||||
|
metadata/_snap_count = 16
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
gradient = SubResource("Gradient_2jwis")
|
|
@ -1,10 +1,12 @@
|
||||||
extends Node3D
|
extends Node3D
|
||||||
|
|
||||||
|
const ConstructRoomMesh = preload ("res://lib/utils/mesh/construct_room_mesh.gd")
|
||||||
|
|
||||||
@onready var wall_corners = $Ceiling/WallCorners
|
@onready var wall_corners = $Ceiling/WallCorners
|
||||||
@onready var wall_edges = $Ceiling/WallEdges
|
@onready var wall_edges = $Ceiling/WallEdges
|
||||||
@onready var wall_mesh: MeshInstance3D = $WallMesh
|
@onready var wall_mesh: MeshInstance3D = $WallMesh
|
||||||
@onready var ceiling_mesh: MeshInstance3D = $CeilingMesh
|
@onready var ceiling_mesh: MeshInstance3D = $CeilingMesh
|
||||||
@onready var wall_collisions = $WallCollisions
|
@onready var wall_collision = $WallCollision/CollisionShape3D
|
||||||
|
|
||||||
@onready var room_floor = $Floor
|
@onready var room_floor = $Floor
|
||||||
@onready var room_ceiling = $Ceiling
|
@onready var room_ceiling = $Ceiling
|
||||||
|
@ -55,75 +57,12 @@ func get_aabb():
|
||||||
return AABB(to_global(min_pos), to_global(max_pos) - to_global(min_pos))
|
return AABB(to_global(min_pos), to_global(max_pos) - to_global(min_pos))
|
||||||
|
|
||||||
static func generate_wall_mesh(room_store: Dictionary):
|
static func generate_wall_mesh(room_store: Dictionary):
|
||||||
if room_store.corners.size() < 2:
|
var corners = room_store.corners
|
||||||
return null
|
var height = room_store.height
|
||||||
|
|
||||||
var st = SurfaceTool.new()
|
return ConstructRoomMesh.generate_wall_mesh(corners, height)
|
||||||
var wall_up = Vector3.UP * room_store.height
|
|
||||||
|
|
||||||
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
|
||||||
|
|
||||||
for corner in room_store.corners:
|
|
||||||
var corner3D = Vector3(corner.x, 0, corner.y)
|
|
||||||
|
|
||||||
st.add_vertex(corner3D)
|
|
||||||
st.add_vertex(corner3D + wall_up)
|
|
||||||
|
|
||||||
var first_corner = Vector3(room_store.corners[0].x, 0, room_store.corners[0].y)
|
|
||||||
|
|
||||||
st.add_vertex(first_corner)
|
|
||||||
st.add_vertex(first_corner + wall_up)
|
|
||||||
|
|
||||||
st.index()
|
|
||||||
st.generate_normals()
|
|
||||||
st.generate_tangents()
|
|
||||||
var mesh = st.commit()
|
|
||||||
|
|
||||||
return mesh
|
|
||||||
|
|
||||||
static func generate_ceiling_mesh(room_store: Dictionary):
|
static func generate_ceiling_mesh(room_store: Dictionary):
|
||||||
|
|
||||||
var points: PackedVector2Array = PackedVector2Array()
|
|
||||||
var edges: PackedInt32Array = PackedInt32Array()
|
|
||||||
var triangles: PackedInt32Array
|
|
||||||
|
|
||||||
var corners = room_store.corners
|
var corners = room_store.corners
|
||||||
|
|
||||||
if corners.size() < 3:
|
return ConstructRoomMesh.generate_ceiling_mesh(corners)
|
||||||
return null
|
|
||||||
|
|
||||||
for i in range(corners.size()):
|
|
||||||
var corner = corners[i]
|
|
||||||
points.append(Vector2(corner.x, corner.y))
|
|
||||||
edges.append(i)
|
|
||||||
edges.append((i + 1) % corners.size())
|
|
||||||
|
|
||||||
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
|
|
||||||
|
|
||||||
cdt.init(true, true, 0.1)
|
|
||||||
|
|
||||||
cdt.insert_vertices(points)
|
|
||||||
cdt.insert_edges(edges)
|
|
||||||
|
|
||||||
cdt.erase_outer_triangles()
|
|
||||||
|
|
||||||
points = cdt.get_all_vertices()
|
|
||||||
triangles = cdt.get_all_triangles()
|
|
||||||
|
|
||||||
var st = SurfaceTool.new()
|
|
||||||
|
|
||||||
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
|
||||||
|
|
||||||
for i in range(points.size()):
|
|
||||||
st.add_vertex(Vector3(points[i].x, 0, points[i].y))
|
|
||||||
|
|
||||||
for i in range(triangles.size()):
|
|
||||||
st.add_index(triangles[i])
|
|
||||||
|
|
||||||
st.index()
|
|
||||||
st.generate_normals()
|
|
||||||
st.generate_tangents()
|
|
||||||
|
|
||||||
var mesh = st.commit()
|
|
||||||
|
|
||||||
return mesh
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
[gd_scene load_steps=9 format=3 uid="uid://bswgmclohuqui"]
|
[gd_scene load_steps=8 format=3 uid="uid://bswgmclohuqui"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://content/system/house/room/room.gd" id="1_fccq0"]
|
[ext_resource type="Script" path="res://content/system/house/room/room.gd" id="1_fccq0"]
|
||||||
[ext_resource type="Script" path="res://content/functions/clickable.gd" id="1_ugebq"]
|
[ext_resource type="Script" path="res://content/functions/clickable.gd" id="1_ugebq"]
|
||||||
[ext_resource type="Script" path="res://lib/utils/state_machine/state_machine.gd" id="4_nbbo6"]
|
[ext_resource type="Script" path="res://lib/utils/state_machine/state_machine.gd" id="4_nbbo6"]
|
||||||
[ext_resource type="Script" path="res://content/system/house/room/states/mini.gd" id="6_g4qca"]
|
|
||||||
[ext_resource type="Script" path="res://content/system/house/room/states/view.gd" id="6_g066t"]
|
[ext_resource type="Script" path="res://content/system/house/room/states/view.gd" id="6_g066t"]
|
||||||
[ext_resource type="Script" path="res://content/system/house/room/states/edit.gd" id="7_ap14h"]
|
[ext_resource type="Script" path="res://content/system/house/room/states/edit.gd" id="7_ap14h"]
|
||||||
|
|
||||||
|
@ -29,7 +28,11 @@ script = ExtResource("1_ugebq")
|
||||||
|
|
||||||
[node name="CeilingMesh" type="MeshInstance3D" parent="."]
|
[node name="CeilingMesh" type="MeshInstance3D" parent="."]
|
||||||
|
|
||||||
[node name="WallCollisions" type="Node3D" parent="."]
|
[node name="WallCollision" type="StaticBody3D" parent="."]
|
||||||
|
collision_layer = 24
|
||||||
|
collision_mask = 0
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="WallCollision"]
|
||||||
|
|
||||||
[node name="Ceiling" type="StaticBody3D" parent="."]
|
[node name="Ceiling" type="StaticBody3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2, 0)
|
||||||
|
@ -57,7 +60,4 @@ script = ExtResource("6_g066t")
|
||||||
[node name="Edit" type="Node" parent="StateMachine"]
|
[node name="Edit" type="Node" parent="StateMachine"]
|
||||||
script = ExtResource("7_ap14h")
|
script = ExtResource("7_ap14h")
|
||||||
|
|
||||||
[node name="Mini" type="Node" parent="StateMachine"]
|
|
||||||
script = ExtResource("6_g4qca")
|
|
||||||
|
|
||||||
[node name="Entities" type="Node3D" parent="."]
|
[node name="Entities" type="Node3D" parent="."]
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
extends RoomState
|
extends RoomState
|
||||||
|
|
||||||
const wall_corner_scene = preload("../wall_corner.tscn")
|
const wall_corner_scene = preload ("../wall_corner.tscn")
|
||||||
const wall_edge_scene = preload("../wall_edge.tscn")
|
const wall_edge_scene = preload ("../wall_edge.tscn")
|
||||||
const RoomState = preload("./room_state.gd")
|
const RoomState = preload ("./room_state.gd")
|
||||||
|
|
||||||
var moving = null
|
var moving = null
|
||||||
var deleting: bool = false
|
var deleting: bool = false
|
||||||
|
@ -27,7 +27,7 @@ func _on_enter():
|
||||||
for i in range(1, corners.size()):
|
for i in range(1, corners.size()):
|
||||||
add_corner(room.to_local(Vector3(corners[i].x, 0, corners[i].y)))
|
add_corner(room.to_local(Vector3(corners[i].x, 0, corners[i].y)))
|
||||||
|
|
||||||
room.room_ceiling.get_node("CollisionShape3D").disabled = (floor_corner == null && height_corner == null)
|
room.room_ceiling.get_node("CollisionShape3D").disabled = (floor_corner == null&&height_corner == null)
|
||||||
room.room_floor.get_node("CollisionShape3D").disabled = false
|
room.room_floor.get_node("CollisionShape3D").disabled = false
|
||||||
|
|
||||||
var ceiling_shape = WorldBoundaryShape3D.new()
|
var ceiling_shape = WorldBoundaryShape3D.new()
|
||||||
|
@ -73,7 +73,7 @@ func remove_corner(index: int):
|
||||||
get_edge(index).queue_free()
|
get_edge(index).queue_free()
|
||||||
|
|
||||||
func _on_click_floor(event):
|
func _on_click_floor(event):
|
||||||
if floor_corner != null && height_corner != null:
|
if floor_corner != null&&height_corner != null:
|
||||||
return
|
return
|
||||||
|
|
||||||
add_floor_corner(event.ray.get_collision_point())
|
add_floor_corner(event.ray.get_collision_point())
|
||||||
|
@ -81,7 +81,7 @@ func _on_click_floor(event):
|
||||||
room.room_ceiling.get_node("CollisionShape3D").disabled = false
|
room.room_ceiling.get_node("CollisionShape3D").disabled = false
|
||||||
|
|
||||||
func _on_click_ceiling(event):
|
func _on_click_ceiling(event):
|
||||||
if floor_corner == null || height_corner == null || event.target != room.room_ceiling:
|
if floor_corner == null||height_corner == null||event.target != room.room_ceiling:
|
||||||
return
|
return
|
||||||
|
|
||||||
var pos = event.ray.get_collision_point()
|
var pos = event.ray.get_collision_point()
|
||||||
|
@ -97,39 +97,39 @@ func add_floor_corner(position: Vector3):
|
||||||
height_edge.align_to_corners(position, position + Vector3.UP * room.room_ceiling.position.y)
|
height_edge.align_to_corners(position, position + Vector3.UP * room.room_ceiling.position.y)
|
||||||
|
|
||||||
floor_corner.get_node("Clickable").on_grab_down.connect(func(event):
|
floor_corner.get_node("Clickable").on_grab_down.connect(func(event):
|
||||||
if !is_active() || moving != null:
|
if !is_active()||moving != null:
|
||||||
return
|
return
|
||||||
|
|
||||||
moving = event.target
|
moving=event.target
|
||||||
)
|
)
|
||||||
|
|
||||||
floor_corner.get_node("Clickable").on_grab_move.connect(func(event):
|
floor_corner.get_node("Clickable").on_grab_move.connect(func(event):
|
||||||
if moving == null:
|
if moving == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
var moving_index = height_corner.get_index()
|
var moving_index=height_corner.get_index()
|
||||||
var direction = -event.ray.global_transform.basis.z
|
var direction=- event.ray.global_transform.basis.z
|
||||||
var new_position = room.room_floor.get_node("CollisionShape3D").shape.plane.intersects_ray(event.ray.global_position, direction)
|
var new_position=room.room_floor.get_node("CollisionShape3D").shape.plane.intersects_ray(event.ray.global_position, direction)
|
||||||
|
|
||||||
if new_position == null:
|
if new_position == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
moving.position = new_position
|
moving.position=new_position
|
||||||
|
|
||||||
height_edge.align_to_corners(new_position, new_position + Vector3.UP * room.room_ceiling.global_position.y)
|
height_edge.align_to_corners(new_position, new_position + Vector3.UP * room.room_ceiling.global_position.y)
|
||||||
|
|
||||||
get_corner(moving_index).position.x = new_position.x
|
get_corner(moving_index).position.x=new_position.x
|
||||||
get_corner(moving_index).position.z = new_position.z
|
get_corner(moving_index).position.z=new_position.z
|
||||||
|
|
||||||
if room.wall_edges.get_child_count() == 0:
|
if room.wall_edges.get_child_count() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
get_edge(moving_index).align_to_corners(new_position, get_corner(moving_index + 1).position)
|
get_edge(moving_index).align_to_corners(new_position, get_corner(moving_index + 1).position)
|
||||||
get_edge(moving_index - 1).align_to_corners(get_corner(moving_index - 1).position, new_position)
|
get_edge(moving_index - 1).align_to_corners(get_corner(moving_index - 1).position, new_position)
|
||||||
)
|
)
|
||||||
|
|
||||||
floor_corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
floor_corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
||||||
moving = null
|
moving=null
|
||||||
)
|
)
|
||||||
|
|
||||||
room.add_child(floor_corner)
|
room.add_child(floor_corner)
|
||||||
|
@ -141,96 +141,95 @@ func add_height_corner(position: Vector3):
|
||||||
height_corner.position.z = position.z
|
height_corner.position.z = position.z
|
||||||
|
|
||||||
height_corner.get_node("Clickable").on_grab_down.connect(func(event):
|
height_corner.get_node("Clickable").on_grab_down.connect(func(event):
|
||||||
if !is_active() || moving != null:
|
if !is_active()||moving != null:
|
||||||
return
|
return
|
||||||
|
|
||||||
moving = event.target
|
moving=event.target
|
||||||
)
|
)
|
||||||
|
|
||||||
height_corner.get_node("Clickable").on_grab_move.connect(func(event):
|
height_corner.get_node("Clickable").on_grab_move.connect(func(event):
|
||||||
if moving == null:
|
if moving == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
var direction = -event.ray.global_transform.basis.z
|
var direction=- event.ray.global_transform.basis.z
|
||||||
var plane_direction = direction
|
var plane_direction=direction
|
||||||
plane_direction.y = 0
|
plane_direction.y=0
|
||||||
plane_direction = plane_direction.normalized() * -1
|
plane_direction=plane_direction.normalized() * - 1
|
||||||
|
|
||||||
var plane = Plane(plane_direction, moving.position)
|
var plane=Plane(plane_direction, moving.position)
|
||||||
|
|
||||||
var new_position = plane.intersects_ray(event.ray.global_position, direction)
|
var new_position=plane.intersects_ray(event.ray.global_position, direction)
|
||||||
|
|
||||||
if new_position == null:
|
if new_position == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
room.room_ceiling.position.y = new_position.y
|
room.room_ceiling.position.y=new_position.y
|
||||||
height_edge.align_to_corners(floor_corner.position, floor_corner.position + Vector3.UP * room.room_ceiling.position.y)
|
height_edge.align_to_corners(floor_corner.position, floor_corner.position + Vector3.UP * room.room_ceiling.position.y)
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
height_corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
height_corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
||||||
moving = null
|
moving=null
|
||||||
)
|
)
|
||||||
|
|
||||||
room.wall_corners.add_child(height_corner)
|
room.wall_corners.add_child(height_corner)
|
||||||
|
|
||||||
func add_corner(position: Vector3, index: int = -1):
|
func add_corner(position: Vector3, index: int=- 1):
|
||||||
var corner = wall_corner_scene.instantiate()
|
var corner = wall_corner_scene.instantiate()
|
||||||
corner.position.x = position.x
|
corner.position.x = position.x
|
||||||
corner.position.z = position.z
|
corner.position.z = position.z
|
||||||
|
|
||||||
corner.get_node("Clickable").on_grab_down.connect(func(event):
|
corner.get_node("Clickable").on_grab_down.connect(func(event):
|
||||||
if !is_active() || moving != null:
|
if !is_active()||moving != null:
|
||||||
return
|
return
|
||||||
|
|
||||||
moving = event.target
|
moving=event.target
|
||||||
)
|
)
|
||||||
|
|
||||||
corner.get_node("Clickable").on_grab_move.connect(func(event):
|
corner.get_node("Clickable").on_grab_move.connect(func(event):
|
||||||
if moving == null:
|
if moving == null:
|
||||||
return
|
return
|
||||||
|
|
||||||
var moving_index = moving.get_index()
|
var moving_index=moving.get_index()
|
||||||
var direction = -event.ray.global_transform.basis.z
|
var direction=- event.ray.global_transform.basis.z
|
||||||
var ceiling_plane = Plane(Vector3.DOWN, room.room_ceiling.global_position)
|
var ceiling_plane=Plane(Vector3.DOWN, room.room_ceiling.global_position)
|
||||||
var new_position = ceiling_plane.intersects_ray(event.ray.global_position, direction)
|
var new_position=ceiling_plane.intersects_ray(event.ray.global_position, direction)
|
||||||
|
|
||||||
if new_position == null:
|
if new_position == null:
|
||||||
deleting = true
|
deleting=true
|
||||||
|
|
||||||
new_position = event.ray.global_position + direction
|
new_position=event.ray.global_position + direction
|
||||||
|
|
||||||
get_corner(moving_index).global_position = new_position
|
get_corner(moving_index).global_position=new_position
|
||||||
|
|
||||||
if room.wall_edges.get_child_count() == 0:
|
if room.wall_edges.get_child_count() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
get_edge(moving_index).align_to_corners(get_corner(moving_index - 1).position, get_corner(moving_index + 1).position)
|
get_edge(moving_index).align_to_corners(get_corner(moving_index - 1).position, get_corner(moving_index + 1).position)
|
||||||
get_edge(moving_index - 1).transform = get_edge(moving_index).transform
|
get_edge(moving_index - 1).transform=get_edge(moving_index).transform
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
deleting = false
|
deleting=false
|
||||||
|
|
||||||
new_position.y = 0
|
new_position.y=0
|
||||||
|
|
||||||
moving.position = new_position
|
moving.position=new_position
|
||||||
|
|
||||||
|
|
||||||
if room.wall_edges.get_child_count() == 0:
|
if room.wall_edges.get_child_count() == 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
get_edge(moving_index).align_to_corners(new_position, get_corner(moving_index + 1).position)
|
get_edge(moving_index).align_to_corners(new_position, get_corner(moving_index + 1).position)
|
||||||
get_edge(moving_index - 1).align_to_corners(get_corner(moving_index - 1).position, new_position)
|
get_edge(moving_index - 1).align_to_corners(get_corner(moving_index - 1).position, new_position)
|
||||||
)
|
)
|
||||||
|
|
||||||
corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
corner.get_node("Clickable").on_grab_up.connect(func(_event):
|
||||||
if deleting:
|
if deleting:
|
||||||
var moving_index = moving.get_index()
|
var moving_index=moving.get_index()
|
||||||
remove_corner(moving_index)
|
remove_corner(moving_index)
|
||||||
|
|
||||||
moving = null
|
moving=null
|
||||||
deleting = false
|
deleting=false
|
||||||
)
|
)
|
||||||
|
|
||||||
room.wall_corners.add_child(corner)
|
room.wall_corners.add_child(corner)
|
||||||
|
@ -243,22 +242,20 @@ func add_corner(position: Vector3, index: int = -1):
|
||||||
|
|
||||||
if num_corners > 2:
|
if num_corners > 2:
|
||||||
if num_corners != room.wall_edges.get_child_count():
|
if num_corners != room.wall_edges.get_child_count():
|
||||||
add_edge(get_corner(-2).position, get_corner(-1).position, -2)
|
add_edge(get_corner( - 2).position, get_corner( - 1).position, -2)
|
||||||
else:
|
else:
|
||||||
get_edge(index - 1).align_to_corners(get_corner(index - 1).position, position)
|
get_edge(index - 1).align_to_corners(get_corner(index - 1).position, position)
|
||||||
|
|
||||||
|
|
||||||
func add_edge(from_pos: Vector3, to_pos: Vector3, index: int = -1):
|
func add_edge(from_pos: Vector3, to_pos: Vector3, index: int=- 1):
|
||||||
var edge: StaticBody3D = wall_edge_scene.instantiate()
|
var edge: StaticBody3D = wall_edge_scene.instantiate()
|
||||||
edge.align_to_corners(from_pos, to_pos)
|
edge.align_to_corners(from_pos, to_pos)
|
||||||
|
|
||||||
edge.get_node("Clickable").on_press_down.connect(func(event):
|
edge.get_node("Clickable").on_press_down.connect(func(event):
|
||||||
var point = event.ray.get_collision_point()
|
var point=event.ray.get_collision_point()
|
||||||
point.y = 0
|
point.y=0
|
||||||
add_corner(point, edge.get_index() + 1)
|
add_corner(point, edge.get_index() + 1)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
room.wall_edges.add_child(edge)
|
room.wall_edges.add_child(edge)
|
||||||
room.wall_edges.move_child(edge, index)
|
room.wall_edges.move_child(edge, index)
|
||||||
return edge
|
return edge
|
||||||
|
@ -277,4 +274,7 @@ func update_store():
|
||||||
store_room.corners = corners
|
store_room.corners = corners
|
||||||
store_room.height = room.room_ceiling.position.y
|
store_room.height = room.room_ceiling.position.y
|
||||||
|
|
||||||
|
# Manually update the array
|
||||||
|
Store.house.state.rooms = Store.house.state.rooms
|
||||||
|
|
||||||
Store.house.save_local()
|
Store.house.save_local()
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
extends RoomState
|
|
||||||
|
|
||||||
const RoomState = preload("./room_state.gd")
|
|
||||||
const walls_mini_material = preload("../walls_mini.tres")
|
|
||||||
const walls_material = preload("../walls.tres")
|
|
||||||
|
|
||||||
func _on_enter():
|
|
||||||
room.wall_mesh.visible = true
|
|
||||||
room.ceiling_mesh.visible = false
|
|
||||||
room.wall_mesh.material_override = walls_mini_material
|
|
||||||
room.room_ceiling.get_node("CollisionShape3D").disabled = true
|
|
||||||
room.room_floor.get_node("CollisionShape3D").disabled = true
|
|
||||||
|
|
||||||
for collision in room.wall_collisions.get_children():
|
|
||||||
collision.get_child(0).disabled = true
|
|
||||||
|
|
||||||
for corner in room.wall_corners.get_children():
|
|
||||||
corner.get_node("CollisionShape3D").disabled = true
|
|
||||||
|
|
||||||
for edge in room.wall_edges.get_children():
|
|
||||||
edge.get_node("CollisionShape3D").disabled = true
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
func _on_leave():
|
|
||||||
room.wall_mesh.visible = false
|
|
||||||
room.ceiling_mesh.visible = false
|
|
||||||
room.wall_mesh.material_override = walls_material
|
|
||||||
room.room_ceiling.get_node("CollisionShape3D").disabled = false
|
|
||||||
room.room_floor.get_node("CollisionShape3D").disabled = false
|
|
||||||
|
|
||||||
for collision in room.wall_collisions.get_children():
|
|
||||||
collision.get_child(0).disabled = false
|
|
||||||
|
|
||||||
for corner in room.wall_corners.get_children():
|
|
||||||
corner.get_node("CollisionShape3D").disabled = false
|
|
||||||
|
|
||||||
for edge in room.wall_edges.get_children():
|
|
||||||
edge.get_node("CollisionShape3D").disabled = false
|
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
extends State
|
extends MachineState
|
||||||
const Room = preload("res://content/system/house/room/room.gd")
|
const Room = preload ("res://content/system/house/room/room.gd")
|
||||||
|
|
||||||
var room: Room
|
var room: Room
|
||||||
|
|
||||||
|
|
|
@ -28,51 +28,13 @@ func _on_enter():
|
||||||
ceiling_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
ceiling_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
||||||
floor_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
floor_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
||||||
ceiling_shape.shape.backface_collision = true
|
ceiling_shape.shape.backface_collision = true
|
||||||
|
|
||||||
var collisions = generate_collision()
|
|
||||||
|
|
||||||
for collision in collisions:
|
var wall_collisions = room.wall_mesh.mesh.create_trimesh_shape()
|
||||||
var static_body = StaticBody3D.new()
|
wall_collisions.backface_collision = true
|
||||||
static_body.set_collision_layer_value(4, true)
|
room.wall_collision.shape = wall_collisions
|
||||||
static_body.set_collision_layer_value(5, true)
|
|
||||||
static_body.collision_mask = 0
|
|
||||||
static_body.add_child(collision)
|
|
||||||
room.wall_collisions.add_child(static_body)
|
|
||||||
|
|
||||||
func _on_leave():
|
func _on_leave():
|
||||||
room.room_ceiling.get_node("CollisionShape3D").disabled = true
|
room.room_ceiling.get_node("CollisionShape3D").disabled = true
|
||||||
room.room_floor.get_node("CollisionShape3D").disabled = true
|
room.room_floor.get_node("CollisionShape3D").disabled = true
|
||||||
|
|
||||||
for collision in room.wall_collisions.get_children():
|
room.wall_collision.shape = null
|
||||||
collision.queue_free()
|
|
||||||
await collision.tree_exited
|
|
||||||
|
|
||||||
func generate_collision():
|
|
||||||
var room_store = Store.house.get_room(room.name)
|
|
||||||
|
|
||||||
var collision_shapes: Array[CollisionShape3D] = []
|
|
||||||
|
|
||||||
var corners = room_store.corners
|
|
||||||
|
|
||||||
for i in range(corners.size()):
|
|
||||||
var corner = Vector3(corners[i].x, 0, corners[i].y)
|
|
||||||
var next_corner_index = (i + 1) % corners.size()
|
|
||||||
var next_corner = Vector3(corners[next_corner_index].x, 0, corners[next_corner_index].y)
|
|
||||||
|
|
||||||
var shape = BoxShape3D.new()
|
|
||||||
shape.size = Vector3((next_corner - corner).length(), room_store.height, 0.04)
|
|
||||||
|
|
||||||
var transform = Transform3D()
|
|
||||||
var back_vector = (corner - next_corner).cross(Vector3.UP).normalized() * shape.size.z / 2
|
|
||||||
|
|
||||||
transform.basis = Basis((next_corner - corner).normalized(), Vector3.UP, back_vector.normalized())
|
|
||||||
transform.origin = corner + (next_corner - corner) / 2 + back_vector + Vector3.UP * shape.size.y / 2
|
|
||||||
|
|
||||||
var collision_shape = CollisionShape3D.new()
|
|
||||||
|
|
||||||
collision_shape.shape = shape
|
|
||||||
collision_shape.transform = transform
|
|
||||||
|
|
||||||
collision_shapes.append(collision_shape)
|
|
||||||
|
|
||||||
return collision_shapes
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
[gd_resource type="StandardMaterial3D" format=3 uid="uid://dxyuncqxagt28"]
|
|
||||||
|
|
||||||
[resource]
|
|
||||||
transparency = 1
|
|
||||||
cull_mode = 2
|
|
||||||
albedo_color = Color(1, 1, 1, 0.47451)
|
|
|
@ -5,6 +5,7 @@ class_name Button3D
|
||||||
|
|
||||||
signal on_button_down()
|
signal on_button_down()
|
||||||
signal on_button_up()
|
signal on_button_up()
|
||||||
|
signal on_toggled(active: bool)
|
||||||
|
|
||||||
const IconFont = preload ("res://assets/icons/icons.tres")
|
const IconFont = preload ("res://assets/icons/icons.tres")
|
||||||
const ECHO_WAIT_INITIAL = 0.5
|
const ECHO_WAIT_INITIAL = 0.5
|
||||||
|
@ -57,6 +58,10 @@ const ECHO_WAIT_REPEAT = 0.1
|
||||||
|
|
||||||
var active: bool = false:
|
var active: bool = false:
|
||||||
set(value):
|
set(value):
|
||||||
|
if active == value:
|
||||||
|
return
|
||||||
|
|
||||||
|
on_toggled.emit(value)
|
||||||
active = value
|
active = value
|
||||||
if !is_inside_tree(): return
|
if !is_inside_tree(): return
|
||||||
update_animation(1.0 if active else 0.0)
|
update_animation(1.0 if active else 0.0)
|
||||||
|
|
|
@ -3,29 +3,29 @@ class_name Tabs3D
|
||||||
|
|
||||||
signal on_select(selected: int)
|
signal on_select(selected: int)
|
||||||
|
|
||||||
var selected: Node3D:
|
var selected = R.state(null)
|
||||||
set(value):
|
|
||||||
if selected == value:
|
|
||||||
return
|
|
||||||
|
|
||||||
if selected != null:
|
|
||||||
selected.active = false
|
|
||||||
|
|
||||||
selected = value
|
|
||||||
selected.active = true
|
|
||||||
on_select.emit(selected.get_index())
|
|
||||||
@export var initial_selected: Node3D
|
@export var initial_selected: Node3D
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
if initial_selected != null:
|
if initial_selected:
|
||||||
selected = initial_selected
|
selected.value = initial_selected
|
||||||
|
|
||||||
|
R.effect(func(_arg):
|
||||||
|
on_select.emit(selected.value)
|
||||||
|
)
|
||||||
|
|
||||||
for option in get_children():
|
for option in get_children():
|
||||||
if option is Button3D == false:
|
if option is Button3D == false:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
option.on_button_down.connect(func():
|
option.on_button_down.connect(func():
|
||||||
selected=option
|
selected.value=option
|
||||||
|
)
|
||||||
|
|
||||||
|
R.effect(func(_arg):
|
||||||
|
option.active=option == selected.value
|
||||||
|
option.disabled=option == selected.value
|
||||||
)
|
)
|
||||||
|
|
||||||
option.toggleable = true
|
option.toggleable = true
|
||||||
|
|
|
@ -8,15 +8,14 @@ var children: Array = []
|
||||||
func _ready():
|
func _ready():
|
||||||
children = get_children()
|
children = get_children()
|
||||||
|
|
||||||
for child in children:
|
for i in range(children.size()):
|
||||||
|
var child = children[i]
|
||||||
child.visible = true
|
child.visible = true
|
||||||
if tabs.selected != null && tabs.selected.get_index() == child.get_index():
|
|
||||||
continue
|
|
||||||
remove_child(child)
|
remove_child(child)
|
||||||
|
|
||||||
tabs.on_select.connect(func(index):
|
R.effect(func(_arg):
|
||||||
for child in get_children():
|
if tabs.selected.value.get_index() == i:
|
||||||
remove_child(child)
|
add_child(child)
|
||||||
|
else:
|
||||||
add_child(children[index])
|
remove_child(child)
|
||||||
)
|
)
|
||||||
|
|
|
@ -32,6 +32,15 @@ func _enter_tree():
|
||||||
func load_devices():
|
func load_devices():
|
||||||
if devices.size() == 0:
|
if devices.size() == 0:
|
||||||
devices = await HomeApi.get_devices()
|
devices = await HomeApi.get_devices()
|
||||||
|
devices.sort_custom(func(a, b):
|
||||||
|
return a.values()[0]["name"].to_lower() < b.values()[0]["name"].to_lower()
|
||||||
|
)
|
||||||
|
|
||||||
|
for device in devices:
|
||||||
|
device.values()[0]["entities"].sort_custom(func(a, b):
|
||||||
|
return a.to_lower() < b.to_lower()
|
||||||
|
)
|
||||||
|
|
||||||
render()
|
render()
|
||||||
|
|
||||||
HomeApi.on_disconnect.connect(func():
|
HomeApi.on_disconnect.connect(func():
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
extends Node3D
|
extends Node3D
|
||||||
|
|
||||||
const Room = preload ("res://content/system/house/room/room.tscn")
|
const Room = preload ("res://content/system/house/room/room.tscn")
|
||||||
const RoomType = preload ("res://content/system/house/room/room.gd")
|
const ConstructRoomMesh = preload ("res://lib/utils/mesh/construct_room_mesh.gd")
|
||||||
|
|
||||||
const material_selected = preload ("../room_selected.tres")
|
const material_selected = preload ("../room_selected.tres")
|
||||||
const material_unselected = preload ("../room_unselected.tres")
|
const material_unselected = preload ("../room_unselected.tres")
|
||||||
|
@ -102,7 +102,7 @@ func _on_click(event: EventPointer):
|
||||||
selected_room = room_name
|
selected_room = room_name
|
||||||
|
|
||||||
func _generate_room_map():
|
func _generate_room_map():
|
||||||
var rooms = Store.house.rooms
|
var rooms = Store.house.state.rooms
|
||||||
|
|
||||||
var target_size = Vector2(0.2, 0.24)
|
var target_size = Vector2(0.2, 0.24)
|
||||||
var target_offset = Vector2(0, 0.05)
|
var target_offset = Vector2(0, 0.05)
|
||||||
|
@ -128,7 +128,7 @@ func _generate_room_map():
|
||||||
current_max.y = max(current_max.y, corner.y)
|
current_max.y = max(current_max.y, corner.y)
|
||||||
|
|
||||||
for room in rooms:
|
for room in rooms:
|
||||||
var mesh = RoomType.generate_ceiling_mesh(room)
|
var mesh = ConstructRoomMesh.generate_ceiling_mesh(room.corners)
|
||||||
|
|
||||||
if mesh == null:
|
if mesh == null:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -3,7 +3,6 @@ extends Node3D
|
||||||
const credits_scene = preload ("./credits.tscn")
|
const credits_scene = preload ("./credits.tscn")
|
||||||
|
|
||||||
@onready var connection_status = $Content/ConnectionStatus
|
@onready var connection_status = $Content/ConnectionStatus
|
||||||
@onready var main = $"/root/Main"
|
|
||||||
|
|
||||||
@onready var input_url = $Content/InputURL
|
@onready var input_url = $Content/InputURL
|
||||||
@onready var input_token = $Content/InputToken
|
@onready var input_token = $Content/InputToken
|
||||||
|
@ -15,6 +14,8 @@ const credits_scene = preload ("./credits.tscn")
|
||||||
@onready var voice_assist = $Content/VoiceAssist
|
@onready var voice_assist = $Content/VoiceAssist
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
|
var settings_store = Store.settings.state
|
||||||
|
|
||||||
background.visible = false
|
background.visible = false
|
||||||
|
|
||||||
credits.on_click.connect(func(_event):
|
credits.on_click.connect(func(_event):
|
||||||
|
@ -25,12 +26,12 @@ func _ready():
|
||||||
)
|
)
|
||||||
|
|
||||||
if Store.settings.is_loaded():
|
if Store.settings.is_loaded():
|
||||||
input_url.text = Store.settings.url
|
input_url.text = settings_store.url
|
||||||
input_token.text = Store.settings.token
|
input_token.text = settings_store.token
|
||||||
else:
|
else:
|
||||||
Store.settings.on_loaded.connect(func():
|
Store.settings.on_loaded.connect(func():
|
||||||
input_url.text=Store.settings.url
|
input_url.text=settings_store.url
|
||||||
input_token.text=Store.settings.token
|
input_token.text=settings_store.token
|
||||||
)
|
)
|
||||||
|
|
||||||
button_connect.on_button_down.connect(func():
|
button_connect.on_button_down.connect(func():
|
||||||
|
@ -39,8 +40,8 @@ func _ready():
|
||||||
|
|
||||||
HomeApi.start_adapter("hass_ws", url, token)
|
HomeApi.start_adapter("hass_ws", url, token)
|
||||||
|
|
||||||
Store.settings.url=url
|
settings_store.url=url
|
||||||
Store.settings.token=token
|
settings_store.token=token
|
||||||
|
|
||||||
Store.settings.save_local()
|
Store.settings.save_local()
|
||||||
)
|
)
|
||||||
|
@ -63,8 +64,7 @@ func _ready():
|
||||||
|
|
||||||
voice_assist.label="mic"
|
voice_assist.label="mic"
|
||||||
|
|
||||||
Store.settings.voice_assistant=true
|
settings_store.voice_assistant=true
|
||||||
main.update_voice_assistant()
|
|
||||||
Store.settings.save_local()
|
Store.settings.save_local()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -74,8 +74,7 @@ func _ready():
|
||||||
|
|
||||||
voice_assist.label="mic_off"
|
voice_assist.label="mic_off"
|
||||||
|
|
||||||
Store.settings.voice_assistant=false
|
settings_store.voice_assistant=false
|
||||||
main.update_voice_assistant()
|
|
||||||
Store.settings.save_local()
|
Store.settings.save_local()
|
||||||
|
|
||||||
)
|
)
|
||||||
|
@ -91,5 +90,9 @@ func _ready():
|
||||||
if Store.settings.is_loaded() == false:
|
if Store.settings.is_loaded() == false:
|
||||||
await Store.settings.on_loaded
|
await Store.settings.on_loaded
|
||||||
|
|
||||||
voice_assist.label = "mic_off" if Store.settings.voice_assistant == false else "mic"
|
var button_label = R.computed(func(_arg):
|
||||||
voice_assist.active = Store.settings.voice_assistant
|
return "mic_off" if settings_store.voice_assistant == false else "mic"
|
||||||
|
)
|
||||||
|
|
||||||
|
R.bind(voice_assist, "label", button_label)
|
||||||
|
R.bind(voice_assist, "active", settings_store, "voice_assistant")
|
||||||
|
|
|
@ -1,12 +1,93 @@
|
||||||
extends Node3D
|
extends Node3D
|
||||||
|
|
||||||
@onready var mini_view = $Content/MiniView
|
const Miniature = preload ("res://content/system/house/mini/miniature.gd")
|
||||||
|
|
||||||
|
@onready var mini_view_button = $Content/MiniView
|
||||||
|
@onready var heat_map_button = $Content/HeatMap
|
||||||
|
@onready var humudity_map_button = $Content/HumidityMap
|
||||||
|
@onready var min_slider = $Content/MinSlider
|
||||||
|
@onready var max_slider = $Content/MaxSlider
|
||||||
|
@onready var opacity_slider = $Content/OpacitySlider
|
||||||
@onready var background = $Background
|
@onready var background = $Background
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
background.visible = false
|
background.visible = false
|
||||||
|
|
||||||
mini_view.on_button_down.connect(func():
|
if !House.body.is_node_ready():
|
||||||
House.body.mini_view = !House.body.mini_view
|
await House.body.ready
|
||||||
|
|
||||||
|
var mini_view = House.body.mini_view
|
||||||
|
|
||||||
|
mini_view_button.on_toggled.connect(func(active):
|
||||||
|
mini_view.small.value=active
|
||||||
)
|
)
|
||||||
|
|
||||||
|
heat_map_button.on_toggled.connect(func(active):
|
||||||
|
if active == false:
|
||||||
|
if mini_view.heatmap_type.value == Miniature.HeatmapType.TEMPERATURE:
|
||||||
|
mini_view.heatmap_type.value=Miniature.HeatmapType.NONE
|
||||||
|
return
|
||||||
|
|
||||||
|
mini_view.heatmap_type.value=Miniature.HeatmapType.TEMPERATURE
|
||||||
|
)
|
||||||
|
|
||||||
|
humudity_map_button.on_toggled.connect(func(active):
|
||||||
|
if active == false:
|
||||||
|
if mini_view.heatmap_type.value == Miniature.HeatmapType.HUMIDITY:
|
||||||
|
mini_view.heatmap_type.value=Miniature.HeatmapType.NONE
|
||||||
|
return
|
||||||
|
|
||||||
|
mini_view.heatmap_type.value=Miniature.HeatmapType.HUMIDITY
|
||||||
|
)
|
||||||
|
|
||||||
|
R.effect(func(_arg):
|
||||||
|
heat_map_button.active=mini_view.heatmap_type.value == Miniature.HeatmapType.TEMPERATURE
|
||||||
|
humudity_map_button.active=mini_view.heatmap_type.value == Miniature.HeatmapType.HUMIDITY
|
||||||
|
)
|
||||||
|
|
||||||
|
min_slider.on_value_changed.connect(func(value):
|
||||||
|
if value >= mini_view.selected_scale.value.y:
|
||||||
|
min_slider.value=mini_view.selected_scale.value.y
|
||||||
|
return
|
||||||
|
|
||||||
|
mini_view.selected_scale.value.x=value
|
||||||
|
)
|
||||||
|
|
||||||
|
max_slider.on_value_changed.connect(func(value):
|
||||||
|
if value <= mini_view.selected_scale.value.x:
|
||||||
|
max_slider.value=mini_view.selected_scale.value.x
|
||||||
|
return
|
||||||
|
|
||||||
|
mini_view.selected_scale.value.y=value
|
||||||
|
)
|
||||||
|
|
||||||
|
R.effect(func(_arg):
|
||||||
|
min_slider.value=mini_view.selected_scale.value.x
|
||||||
|
max_slider.value=mini_view.selected_scale.value.y
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update Slider
|
||||||
|
R.effect(func(_arg):
|
||||||
|
var minmax=mini_view.get_base_scale()
|
||||||
|
min_slider.min=minmax.x
|
||||||
|
min_slider.max=minmax.y
|
||||||
|
|
||||||
|
max_slider.min=minmax.x
|
||||||
|
max_slider.max=minmax.y
|
||||||
|
|
||||||
|
var sensor_minmax=mini_view.get_sensor_scale()
|
||||||
|
|
||||||
|
sensor_minmax.x=floor(sensor_minmax.x)
|
||||||
|
sensor_minmax.y=ceil(sensor_minmax.y)
|
||||||
|
|
||||||
|
mini_view.selected_scale.value=sensor_minmax
|
||||||
|
min_slider.value=sensor_minmax.x
|
||||||
|
max_slider.value=sensor_minmax.y
|
||||||
|
|
||||||
|
var unit=mini_view.get_sensor_unit()
|
||||||
|
|
||||||
|
min_slider.label_unit=unit
|
||||||
|
max_slider.label_unit=unit
|
||||||
|
)
|
||||||
|
|
||||||
|
R.bind(opacity_slider, "value", mini_view.opacity, opacity_slider.on_value_changed)
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
[gd_scene load_steps=4 format=3 uid="uid://ddpxthb414unp"]
|
[gd_scene load_steps=6 format=3 uid="uid://ddpxthb414unp"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://content/ui/menu/view/view_menu.gd" id="1_hxajx"]
|
[ext_resource type="Script" path="res://content/ui/menu/view/view_menu.gd" id="1_hxajx"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="2_qan1b"]
|
[ext_resource type="PackedScene" uid="uid://bsjqdvkt0u87c" path="res://content/ui/components/button/button.tscn" id="2_qan1b"]
|
||||||
|
[ext_resource type="Material" uid="uid://bujy3egn1oqac" path="res://assets/materials/pri-500.material" id="2_se6ic"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://pk5k1q8bx0rj" path="res://content/ui/components/slider/slider.tscn" id="4_d3xhb"]
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_qr3bi"]
|
[sub_resource type="BoxMesh" id="BoxMesh_qr3bi"]
|
||||||
size = Vector3(0.3, 0.01, 0.3)
|
size = Vector3(0.3, 0.01, 0.3)
|
||||||
|
@ -11,6 +13,7 @@ script = ExtResource("1_hxajx")
|
||||||
|
|
||||||
[node name="Background" type="MeshInstance3D" parent="."]
|
[node name="Background" type="MeshInstance3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.15, 0, 0.15)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.15, 0, 0.15)
|
||||||
|
material_override = ExtResource("2_se6ic")
|
||||||
mesh = SubResource("BoxMesh_qr3bi")
|
mesh = SubResource("BoxMesh_qr3bi")
|
||||||
|
|
||||||
[node name="Content" type="Node3D" parent="."]
|
[node name="Content" type="Node3D" parent="."]
|
||||||
|
@ -19,3 +22,85 @@ mesh = SubResource("BoxMesh_qr3bi")
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.03, 0, 0.03)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.03, 0, 0.03)
|
||||||
label = "view_comfy"
|
label = "view_comfy"
|
||||||
icon = true
|
icon = true
|
||||||
|
toggleable = true
|
||||||
|
|
||||||
|
[node name="MiniViewLabel" type="Label3D" parent="Content"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0.07, 0.01, 0.03)
|
||||||
|
pixel_size = 0.001
|
||||||
|
text = "Mini View"
|
||||||
|
font_size = 18
|
||||||
|
outline_size = 0
|
||||||
|
horizontal_alignment = 0
|
||||||
|
|
||||||
|
[node name="HeatMap" parent="Content" instance=ExtResource("2_qan1b")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.03, 0, 0.09)
|
||||||
|
label = "device_thermostat"
|
||||||
|
icon = true
|
||||||
|
toggleable = true
|
||||||
|
|
||||||
|
[node name="HeatMapLabel" type="Label3D" parent="Content"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0.07, 0.01, 0.09)
|
||||||
|
pixel_size = 0.001
|
||||||
|
text = "Heat Map"
|
||||||
|
font_size = 18
|
||||||
|
outline_size = 0
|
||||||
|
horizontal_alignment = 0
|
||||||
|
|
||||||
|
[node name="HumidityMap" parent="Content" instance=ExtResource("2_qan1b")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.03, 0, 0.15)
|
||||||
|
label = "humidity_mid"
|
||||||
|
icon = true
|
||||||
|
toggleable = true
|
||||||
|
|
||||||
|
[node name="HumidityMapLabel" type="Label3D" parent="Content"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0.07, 0.01, 0.15)
|
||||||
|
pixel_size = 0.001
|
||||||
|
text = "Humidity Map"
|
||||||
|
font_size = 18
|
||||||
|
outline_size = 0
|
||||||
|
horizontal_alignment = 0
|
||||||
|
|
||||||
|
[node name="MinSlider" parent="Content" instance=ExtResource("4_d3xhb")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.06, 0.01, 0.2)
|
||||||
|
step = 1.0
|
||||||
|
show_label = true
|
||||||
|
size = Vector3(10, 0.4, 1)
|
||||||
|
|
||||||
|
[node name="MinValue" type="Label3D" parent="Content"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0.15, 0.01, 0.2)
|
||||||
|
pixel_size = 0.001
|
||||||
|
text = "Min Value"
|
||||||
|
font_size = 18
|
||||||
|
outline_size = 0
|
||||||
|
horizontal_alignment = 0
|
||||||
|
|
||||||
|
[node name="MaxSlider" parent="Content" instance=ExtResource("4_d3xhb")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.06, 0.01, 0.24)
|
||||||
|
step = 1.0
|
||||||
|
show_label = true
|
||||||
|
size = Vector3(10, 0.4, 1)
|
||||||
|
|
||||||
|
[node name="MaxValue" type="Label3D" parent="Content"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0.15, 0.01, 0.24)
|
||||||
|
pixel_size = 0.001
|
||||||
|
text = "Max Value"
|
||||||
|
font_size = 18
|
||||||
|
outline_size = 0
|
||||||
|
horizontal_alignment = 0
|
||||||
|
|
||||||
|
[node name="OpacitySlider" parent="Content" instance=ExtResource("4_d3xhb")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.06, 0.01, 0.28)
|
||||||
|
max = 100.0
|
||||||
|
value = 30.0
|
||||||
|
step = 10.0
|
||||||
|
show_label = true
|
||||||
|
label_unit = "%"
|
||||||
|
size = Vector3(10, 0.4, 1)
|
||||||
|
|
||||||
|
[node name="OpacityLabel" type="Label3D" parent="Content"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0, 1, 0, -1, 0, 0.15, 0.01, 0.28)
|
||||||
|
pixel_size = 0.001
|
||||||
|
text = "Opacity"
|
||||||
|
font_size = 18
|
||||||
|
outline_size = 0
|
||||||
|
horizontal_alignment = 0
|
||||||
|
|
|
@ -9,7 +9,7 @@ func _ready():
|
||||||
if Store.settings.is_loaded() == false:
|
if Store.settings.is_loaded() == false:
|
||||||
await Store.settings.on_loaded
|
await Store.settings.on_loaded
|
||||||
|
|
||||||
if (Store.settings.url != ""&&Store.settings.url != null)||Store.settings.onboarding_complete:
|
if (Store.settings.state.url != ""&&Store.settings.state.url != null)||Store.settings.state.onboarding_complete:
|
||||||
close()
|
close()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ func _ready():
|
||||||
EventSystem.on_slow_tick.connect(_slow_tick)
|
EventSystem.on_slow_tick.connect(_slow_tick)
|
||||||
|
|
||||||
func close():
|
func close():
|
||||||
Store.settings.onboarding_complete = true
|
Store.settings.state.onboarding_complete = true
|
||||||
Store.settings.save_local()
|
Store.settings.save_local()
|
||||||
queue_free()
|
queue_free()
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,8 @@ func _ready():
|
||||||
var success = Store.settings.load_local()
|
var success = Store.settings.load_local()
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
start_adapter(Store.settings.type.to_lower(), Store.settings.url, Store.settings.token)
|
print(Store.settings)
|
||||||
|
start_adapter(Store.settings.state.type.to_lower(), Store.settings.state.url, Store.settings.state.token)
|
||||||
|
|
||||||
## Starts the adapter for the given type and url
|
## Starts the adapter for the given type and url
|
||||||
func start_adapter(type: String, url: String, token: String):
|
func start_adapter(type: String, url: String, token: String):
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
extends Node
|
extends Node
|
||||||
## Shortcut to get the House node from the Main scene
|
## Shortcut to get the House node from the Main scene
|
||||||
|
|
||||||
@onready var body = get_node_or_null("/root/Main/House")
|
const HouseClass = preload ("res://content/system/house/house.gd")
|
||||||
|
|
||||||
|
@onready var body: HouseClass = get_node_or_null("/root/Main/House")
|
|
@ -1,5 +1,8 @@
|
||||||
extends StoreClass
|
extends StoreClass
|
||||||
const StoreClass = preload("./store.gd")
|
const StoreClass = preload ("./store.gd")
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
self.state = R.state({})
|
||||||
|
|
||||||
func clear():
|
func clear():
|
||||||
pass
|
pass
|
|
@ -3,29 +3,31 @@ extends StoreClass
|
||||||
|
|
||||||
const StoreClass = preload ("./store.gd")
|
const StoreClass = preload ("./store.gd")
|
||||||
|
|
||||||
## Type Room
|
|
||||||
## name: String
|
|
||||||
## corners: Vec2[]
|
|
||||||
## height: float
|
|
||||||
var rooms = []
|
|
||||||
## Type Entity
|
|
||||||
## id: String
|
|
||||||
## position: Vec3
|
|
||||||
## rotation: Vec3
|
|
||||||
## room: String
|
|
||||||
var entities = []
|
|
||||||
var align_position1: Vector3
|
|
||||||
var align_position2: Vector3
|
|
||||||
|
|
||||||
func _init():
|
func _init():
|
||||||
_save_path = "user://house.json"
|
_save_path = "user://house.json"
|
||||||
|
|
||||||
|
self.state = R.store({
|
||||||
|
## Type Room
|
||||||
|
## name: String
|
||||||
|
## corners: Vec2[]
|
||||||
|
## height: float
|
||||||
|
"rooms": [],
|
||||||
|
## Type Entity
|
||||||
|
## id: String
|
||||||
|
## position: Vec3
|
||||||
|
## rotation: Vec3
|
||||||
|
## room: String
|
||||||
|
"entities": [],
|
||||||
|
"align_position1": Vector3(),
|
||||||
|
"align_position2": Vector3()
|
||||||
|
})
|
||||||
|
|
||||||
func clear():
|
func clear():
|
||||||
rooms = []
|
self.state.rooms = []
|
||||||
entities = []
|
self.state.entities = []
|
||||||
|
|
||||||
func get_room(name):
|
func get_room(name):
|
||||||
for room in rooms:
|
for room in self.state.rooms:
|
||||||
if room.name == name:
|
if room.name == name:
|
||||||
return room
|
return room
|
||||||
return null
|
return null
|
|
@ -3,23 +3,23 @@ extends StoreClass
|
||||||
|
|
||||||
const StoreClass = preload ("./store.gd")
|
const StoreClass = preload ("./store.gd")
|
||||||
|
|
||||||
## The adapter to use for connecting with a backend
|
|
||||||
var type: String = "HASS_WS"
|
|
||||||
var url: String = ""
|
|
||||||
var token: String = ""
|
|
||||||
|
|
||||||
## If the voice assistant should be enabled
|
|
||||||
var voice_assistant: bool = false
|
|
||||||
|
|
||||||
## If the onboarding process has been completed
|
|
||||||
var onboarding_complete: bool = false
|
|
||||||
|
|
||||||
func _init():
|
func _init():
|
||||||
_save_path = "user://settings.json"
|
_save_path = "user://settings.json"
|
||||||
|
|
||||||
|
self.state = R.store({
|
||||||
|
## The adapter to use for connecting with a backend
|
||||||
|
"type": "HASS_WS",
|
||||||
|
"url": "",
|
||||||
|
"token": "",
|
||||||
|
## If the voice assistant should be enabled
|
||||||
|
"voice_assistant": false,
|
||||||
|
## If the onboarding process has been completed
|
||||||
|
"onboarding_complete": false
|
||||||
|
})
|
||||||
|
|
||||||
func clear():
|
func clear():
|
||||||
type = "HASS_WS"
|
self.state.type = "HASS_WS"
|
||||||
url = ""
|
self.state.url = ""
|
||||||
token = ""
|
self.state.token = ""
|
||||||
voice_assistant = false
|
self.state.voice_assistant = false
|
||||||
onboarding_complete = false
|
self.state.onboarding_complete = false
|
||||||
|
|
|
@ -9,6 +9,7 @@ signal on_loaded
|
||||||
## Signal emitted when the data is saved.
|
## Signal emitted when the data is saved.
|
||||||
signal on_saved
|
signal on_saved
|
||||||
|
|
||||||
|
var state: RdotStore
|
||||||
var _loaded = false
|
var _loaded = false
|
||||||
var _save_path = null
|
var _save_path = null
|
||||||
|
|
||||||
|
@ -20,44 +21,42 @@ func is_loaded():
|
||||||
func clear():
|
func clear():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
func create_dict():
|
func sanitizeState(dict=state):
|
||||||
var data: Dictionary = {}
|
var data = {}
|
||||||
|
|
||||||
for prop_info in get_property_list():
|
for prop_info in state.get_property_list():
|
||||||
if prop_info.name.begins_with("_")||prop_info.hint_string != "":
|
var key = prop_info.name
|
||||||
|
|
||||||
|
if key.begins_with("_")||(prop_info.has("hint_string")&&prop_info.hint_string != ""):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var prop = get(prop_info.name)
|
if dict[key] is Dictionary:
|
||||||
|
data[key] = sanitizeState(dict[key])
|
||||||
if prop is Store:
|
|
||||||
data[prop_info.name] = prop.create_dict()
|
|
||||||
else:
|
else:
|
||||||
data[prop_info.name] = VariantSerializer.stringify_value(prop)
|
data[key] = VariantSerializer.stringify_value(dict[key])
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
func use_dict(dict: Dictionary):
|
func use_dict(dict: Dictionary, target=state):
|
||||||
for prop_info in get_property_list():
|
for prop_info in state.get_property_list():
|
||||||
if prop_info.name.begins_with("_")||prop_info.hint_string != "":
|
var key = prop_info.name
|
||||||
|
|
||||||
|
if key.begins_with("_")||(prop_info.has("hint_string")&&prop_info.hint_string != ""):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var prop = get(prop_info.name)
|
if dict.has(key) == false:
|
||||||
|
|
||||||
if dict.has(prop_info.name) == false:
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
var prop_value = dict[prop_info.name]
|
if target[key] is Dictionary:
|
||||||
|
use_dict(dict[key], target[key])
|
||||||
if prop is Store:
|
|
||||||
prop.use_dict(prop_value)
|
|
||||||
else:
|
else:
|
||||||
set(prop_info.name, prop_value)
|
target[key] = dict[key]
|
||||||
|
|
||||||
func save_local(path=_save_path):
|
func save_local(path=_save_path):
|
||||||
if path == null:
|
if path == null:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
var data = create_dict()
|
var data = sanitizeState()
|
||||||
|
|
||||||
var save_file = FileAccess.open(path, FileAccess.WRITE)
|
var save_file = FileAccess.open(path, FileAccess.WRITE)
|
||||||
|
|
||||||
|
|
208
app/lib/utils/mesh/construct_room_mesh.gd
Normal file
208
app/lib/utils/mesh/construct_room_mesh.gd
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
static func generate_wall_mesh(corners, height):
|
||||||
|
if corners.size() < 3:
|
||||||
|
return null
|
||||||
|
|
||||||
|
var st = SurfaceTool.new()
|
||||||
|
var wall_up = Vector3.UP * height
|
||||||
|
|
||||||
|
st.begin(Mesh.PRIMITIVE_TRIANGLE_STRIP)
|
||||||
|
|
||||||
|
for corner in corners:
|
||||||
|
var corner3D = Vector3(corner.x, 0, corner.y)
|
||||||
|
|
||||||
|
st.add_vertex(corner3D + wall_up)
|
||||||
|
st.add_vertex(corner3D)
|
||||||
|
|
||||||
|
var first_corner = Vector3(corners[0].x, 0, corners[0].y)
|
||||||
|
|
||||||
|
st.add_vertex(first_corner + wall_up)
|
||||||
|
st.add_vertex(first_corner)
|
||||||
|
|
||||||
|
st.index()
|
||||||
|
st.generate_normals()
|
||||||
|
st.generate_tangents()
|
||||||
|
var mesh = st.commit()
|
||||||
|
|
||||||
|
return mesh
|
||||||
|
|
||||||
|
static func generate_ceiling_mesh(corners):
|
||||||
|
var points: PackedVector2Array = PackedVector2Array()
|
||||||
|
var edges: PackedInt32Array = PackedInt32Array()
|
||||||
|
var triangles: PackedInt32Array
|
||||||
|
|
||||||
|
if corners.size() < 3:
|
||||||
|
return null
|
||||||
|
|
||||||
|
for i in range(corners.size()):
|
||||||
|
var corner = corners[i]
|
||||||
|
points.append(Vector2(corner.x, corner.y))
|
||||||
|
edges.append(i)
|
||||||
|
edges.append((i + 1) % corners.size())
|
||||||
|
|
||||||
|
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
|
||||||
|
|
||||||
|
cdt.init(true, true, 0.1)
|
||||||
|
|
||||||
|
cdt.insert_vertices(points)
|
||||||
|
cdt.insert_edges(edges)
|
||||||
|
|
||||||
|
cdt.erase_outer_triangles()
|
||||||
|
|
||||||
|
points = cdt.get_all_vertices()
|
||||||
|
triangles = cdt.get_all_triangles()
|
||||||
|
|
||||||
|
return _create_mesh(points, triangles)
|
||||||
|
|
||||||
|
static func generate_wall_mesh_grid(corners, height, grid: Vector2=Vector2(0.1, 0.1)):
|
||||||
|
if corners.size() < 3:
|
||||||
|
return null
|
||||||
|
|
||||||
|
var st = SurfaceTool.new()
|
||||||
|
|
||||||
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
|
||||||
|
for corner_i in range(corners.size()):
|
||||||
|
var corner = Vector3(corners[corner_i].x, 0, corners[corner_i].y)
|
||||||
|
var next_index = (corner_i + 1) % corners.size()
|
||||||
|
var next_corner = Vector3(corners[next_index].x, 0, corners[next_index].y)
|
||||||
|
|
||||||
|
var steps = ceil(Vector2((next_corner - corner).length() / grid.x, height / grid.y))
|
||||||
|
|
||||||
|
var forward_dir = (next_corner - corner).normalized() * grid.x
|
||||||
|
var up_dir = Vector3.UP * grid.y
|
||||||
|
|
||||||
|
var close_distance = Vector2(1, 1)
|
||||||
|
|
||||||
|
for y in range(0, steps.y):
|
||||||
|
|
||||||
|
close_distance.x = 1
|
||||||
|
|
||||||
|
if y == steps.y - 1:
|
||||||
|
close_distance.y = fmod(height, grid.y) / grid.y
|
||||||
|
|
||||||
|
for x in range(0, steps.x):
|
||||||
|
var point = corner + forward_dir * x + Vector3.UP * grid.y * y
|
||||||
|
|
||||||
|
if x == steps.x - 1:
|
||||||
|
close_distance.x = fmod(corner.distance_to(next_corner), grid.x) / grid.x
|
||||||
|
|
||||||
|
st.add_vertex(point)
|
||||||
|
st.add_vertex(point + forward_dir * close_distance.x)
|
||||||
|
st.add_vertex(point + up_dir * close_distance.y)
|
||||||
|
|
||||||
|
st.add_vertex(point + forward_dir * close_distance.x)
|
||||||
|
st.add_vertex(point + forward_dir * close_distance.x + up_dir * close_distance.y)
|
||||||
|
st.add_vertex(point + up_dir * close_distance.y)
|
||||||
|
|
||||||
|
st.index()
|
||||||
|
st.generate_normals()
|
||||||
|
st.generate_tangents()
|
||||||
|
var mesh = st.commit()
|
||||||
|
|
||||||
|
return mesh
|
||||||
|
|
||||||
|
static func generate_ceiling_mesh_grid(corners, grid: Vector2=Vector2(0.1, 0.1)):
|
||||||
|
var points: PackedVector2Array = PackedVector2Array()
|
||||||
|
var edges: PackedInt32Array = PackedInt32Array()
|
||||||
|
var triangles: PackedInt32Array
|
||||||
|
|
||||||
|
if corners.size() < 3:
|
||||||
|
return null
|
||||||
|
|
||||||
|
var min_val = Vector2(corners[0])
|
||||||
|
var max_val = Vector2(corners[0])
|
||||||
|
|
||||||
|
for i in range(corners.size()):
|
||||||
|
var corner = corners[i]
|
||||||
|
|
||||||
|
min_val.x = min(min_val.x, corner.x)
|
||||||
|
min_val.y = min(min_val.y, corner.y)
|
||||||
|
max_val.x = max(max_val.x, corner.x)
|
||||||
|
max_val.y = max(max_val.y, corner.y)
|
||||||
|
|
||||||
|
points.append(Vector2(corner.x, corner.y))
|
||||||
|
edges.append(i)
|
||||||
|
edges.append((i + 1) % corners.size())
|
||||||
|
|
||||||
|
var size = max_val - min_val
|
||||||
|
|
||||||
|
# Subdivide edges to grid
|
||||||
|
for i in range(corners.size()):
|
||||||
|
var corner = corners[i]
|
||||||
|
var next_index = (i + 1) % corners.size()
|
||||||
|
var next_corner = corners[next_index]
|
||||||
|
|
||||||
|
var steps = ceil(Vector2((next_corner - corner).length() / grid.x, size.y / grid.y))
|
||||||
|
|
||||||
|
var forward_dir = (next_corner - corner).normalized() * grid.x
|
||||||
|
|
||||||
|
for x in range(1, steps.x):
|
||||||
|
var point = corner + forward_dir * x
|
||||||
|
|
||||||
|
points.append(Vector2(point.x, point.y))
|
||||||
|
|
||||||
|
## Fill points insde the polygon
|
||||||
|
for y in range(1, int(size.y / grid.y)):
|
||||||
|
var x_intersections: Array[float] = []
|
||||||
|
|
||||||
|
var y_start = Vector2(min_val.x, min_val.y + y * grid.y)
|
||||||
|
var y_end = Vector2(max_val.x, min_val.y + y * grid.y)
|
||||||
|
|
||||||
|
for i in range(corners.size()):
|
||||||
|
var a = corners[i]
|
||||||
|
var b = corners[(i + 1) % corners.size()]
|
||||||
|
|
||||||
|
var result = Geometry2D.segment_intersects_segment(a, b, y_start, y_end)
|
||||||
|
|
||||||
|
if result != null:
|
||||||
|
x_intersections.append(result.x)
|
||||||
|
|
||||||
|
var intersection_counter = 0
|
||||||
|
|
||||||
|
x_intersections.sort()
|
||||||
|
|
||||||
|
for x in range(1, int(size.x / grid.x)):
|
||||||
|
var point = min_val + Vector2(x * grid.x, y * grid.y)
|
||||||
|
|
||||||
|
for i in range(intersection_counter, x_intersections.size()):
|
||||||
|
if x_intersections[i] < point.x:
|
||||||
|
intersection_counter += 1
|
||||||
|
|
||||||
|
var color = Color(1, 1, 0)
|
||||||
|
|
||||||
|
if intersection_counter % 2 == 1:
|
||||||
|
color = Color(1, 0, 1)
|
||||||
|
points.append(point)
|
||||||
|
|
||||||
|
var cdt: ConstrainedTriangulation = ConstrainedTriangulation.new()
|
||||||
|
|
||||||
|
cdt.init(true, true, 0.1)
|
||||||
|
|
||||||
|
cdt.insert_vertices(points)
|
||||||
|
cdt.insert_edges(edges)
|
||||||
|
|
||||||
|
cdt.erase_outer_triangles()
|
||||||
|
|
||||||
|
points = cdt.get_all_vertices()
|
||||||
|
triangles = cdt.get_all_triangles()
|
||||||
|
|
||||||
|
return _create_mesh(points, triangles)
|
||||||
|
|
||||||
|
static func _create_mesh(points: PackedVector2Array, triangles: PackedInt32Array):
|
||||||
|
var st = SurfaceTool.new()
|
||||||
|
|
||||||
|
st.begin(Mesh.PRIMITIVE_TRIANGLES)
|
||||||
|
|
||||||
|
for i in range(points.size()):
|
||||||
|
st.add_vertex(Vector3(points[i].x, 0, points[i].y))
|
||||||
|
|
||||||
|
for i in range(triangles.size()):
|
||||||
|
st.add_index(triangles[i])
|
||||||
|
|
||||||
|
st.index()
|
||||||
|
st.generate_normals()
|
||||||
|
st.generate_tangents()
|
||||||
|
|
||||||
|
var mesh = st.commit()
|
||||||
|
|
||||||
|
return mesh
|
|
@ -1,6 +1,6 @@
|
||||||
extends Node
|
extends Node
|
||||||
## Base Class for all states
|
## Base Class for all states
|
||||||
class_name State
|
class_name MachineState
|
||||||
|
|
||||||
var state_machine: StateMachine
|
var state_machine: StateMachine
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user