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/Trigger/node = SubResource("5")
|
||||
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/Trigger/node = SubResource("5")
|
||||
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/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -22,6 +22,7 @@ meshes/generate_lods=true
|
|||
meshes/create_shadow_meshes=true
|
||||
meshes/light_baking=1
|
||||
meshes/lightmap_texel_size=0.2
|
||||
meshes/force_disable_compression=false
|
||||
skins/use_named_skins=true
|
||||
animation/import=true
|
||||
animation/fps=30
|
||||
|
@ -29,4 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=0
|
||||
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
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=1
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -30,5 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=1
|
||||
gltf/naming_version=0
|
||||
gltf/embedded_image_handling=1
|
||||
|
|
|
@ -30,5 +30,5 @@ animation/trimming=false
|
|||
animation/remove_immutable_tracks=true
|
||||
import_script/path=""
|
||||
_subresources={}
|
||||
gltf/naming_version=1
|
||||
gltf/naming_version=0
|
||||
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 collision_shape = $CollisionShape3D
|
||||
|
||||
var sensor_data = {}
|
||||
var unit = null
|
||||
|
||||
# Called when the node enters the scene tree for the first time.
|
||||
func _ready():
|
||||
super()
|
||||
|
@ -17,14 +20,21 @@ func _ready():
|
|||
)
|
||||
|
||||
func set_text(stateInfo):
|
||||
if stateInfo == null:
|
||||
return
|
||||
|
||||
var text = stateInfo["state"]
|
||||
|
||||
if stateInfo["attributes"]["friendly_name"] != null:
|
||||
text = stateInfo["attributes"]["friendly_name"] + "\n" + text
|
||||
|
||||
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"]
|
||||
|
||||
if stateInfo["attributes"].has("device_class"):
|
||||
sensor_data[stateInfo["attributes"]["device_class"]] = stateInfo["state"]
|
||||
|
||||
label.text = text
|
||||
|
||||
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.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:
|
||||
RenderingServer.set_debug_generate_wireframes(true)
|
||||
|
||||
update_voice_assistant()
|
||||
create_voice_assistant()
|
||||
|
||||
controller_left.button_pressed.connect(func(name):
|
||||
_emit_action(name, true, false)
|
||||
|
@ -47,7 +47,7 @@ func _ready():
|
|||
if action.name == "menu_button":
|
||||
toggle_menu()
|
||||
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):
|
||||
|
@ -65,16 +65,20 @@ func _ready():
|
|||
remove_child(keyboard)
|
||||
)
|
||||
|
||||
func update_voice_assistant():
|
||||
func create_voice_assistant():
|
||||
if Store.settings.is_loaded() == false:
|
||||
await Store.settings.on_loaded
|
||||
|
||||
if Store.settings.voice_assistant&&voice_assistant == null:
|
||||
var settings_store = Store.settings.state
|
||||
|
||||
R.effect(func(_arg):
|
||||
if settings_store.voice_assistant == true&&voice_assistant == null:
|
||||
voice_assistant=VoiceAssistant.instantiate()
|
||||
add_child(voice_assistant)
|
||||
elif !Store.settings.voice_assistant&&voice_assistant != null:
|
||||
elif settings_store.voice_assistant == false&&voice_assistant != null:
|
||||
remove_child(voice_assistant)
|
||||
voice_assistant.queue_free()
|
||||
)
|
||||
|
||||
func toggle_menu():
|
||||
if menu.show_menu == false:
|
||||
|
|
|
@ -48,8 +48,8 @@ func get_new_transform():
|
|||
return marker.global_transform
|
||||
|
||||
func update_align_reference():
|
||||
corner1.global_position = Store.house.align_position1
|
||||
corner2.global_position = Store.house.align_position2
|
||||
corner1.global_position = Store.house.state.align_position1
|
||||
corner2.global_position = Store.house.state.align_position2
|
||||
|
||||
corner2.look_at(corner1.global_position, Vector3.UP)
|
||||
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)
|
||||
|
||||
func update_store():
|
||||
Store.house.align_position1 = corner1.global_position
|
||||
Store.house.align_position2 = corner2.global_position
|
||||
Store.house.state.align_position1 = corner1.global_position
|
||||
Store.house.state.align_position2 = corner2.global_position
|
|
@ -2,17 +2,16 @@ extends Node3D
|
|||
|
||||
const Room = preload ("./room/room.tscn")
|
||||
const RoomType = preload ("./room/room.gd")
|
||||
const Miniature = preload ("./mini/miniature.gd")
|
||||
const AlignReference = preload ("./align_reference.gd")
|
||||
|
||||
@onready var levels = $Levels
|
||||
@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 editing_room: RoomType = null
|
||||
var mini_view: bool = false:
|
||||
set(value):
|
||||
mini_view = value
|
||||
update_mini_view()
|
||||
|
||||
func _ready():
|
||||
Store.house.on_loaded.connect(func():
|
||||
|
@ -26,18 +25,18 @@ func update_house():
|
|||
|
||||
align_reference.update_align_reference()
|
||||
|
||||
for index in range(Store.house.rooms.size() - 1, -1, -1):
|
||||
var new_room = Store.house.rooms[index]
|
||||
for index in range(Store.house.state.rooms.size()):
|
||||
var new_room = Store.house.state.rooms[index]
|
||||
|
||||
if new_room.corners.size() == 0:
|
||||
Store.house.rooms.remove_at(index)
|
||||
Store.house.state.rooms.remove_at(index)
|
||||
Store.house.save_local()
|
||||
continue
|
||||
|
||||
create_room(new_room.name, 0)
|
||||
|
||||
for entity_index in range(Store.house.entities.size()):
|
||||
var entity = Store.house.entities[entity_index]
|
||||
for entity_index in range(Store.house.state.entities.size()):
|
||||
var entity = Store.house.state.entities[entity_index]
|
||||
|
||||
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)
|
||||
|
||||
if existing_room == null:
|
||||
Store.house.rooms.append({
|
||||
Store.house.state.rooms.append({
|
||||
"name": room_name,
|
||||
"height": 2.0,
|
||||
"corners": [],
|
||||
|
@ -101,7 +100,7 @@ func delete_room(room_name):
|
|||
var store_room = Store.house.get_room(room_name)
|
||||
|
||||
if store_room != null:
|
||||
Store.house.rooms.erase(store_room)
|
||||
Store.house.state.rooms.erase(store_room)
|
||||
|
||||
Store.house.save_local()
|
||||
|
||||
|
@ -198,44 +197,6 @@ func create_entity_in(entity_id: String, room_name: String):
|
|||
|
||||
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():
|
||||
fixing_reference = false
|
||||
align_reference.visible = true
|
||||
|
@ -270,7 +231,7 @@ func save_reference():
|
|||
Store.house.save_local()
|
||||
|
||||
func save_all_entities():
|
||||
Store.house.entities.clear()
|
||||
Store.house.state.entities.clear()
|
||||
|
||||
for room in get_rooms(0):
|
||||
for entity in room.get_node("Entities").get_children():
|
||||
|
@ -281,6 +242,6 @@ func save_all_entities():
|
|||
"room": String(room.name)
|
||||
}
|
||||
|
||||
Store.house.entities.append(entity_data)
|
||||
Store.house.state.entities.append(entity_data)
|
||||
|
||||
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/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://ds60i5n211hi3" path="res://content/system/house/mini/miniature.tscn" id="4_qjbly"]
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_x81up"]
|
||||
|
||||
|
@ -22,6 +23,8 @@ script = ExtResource("2_w1auk")
|
|||
restricted = true
|
||||
lock_rotation = true
|
||||
|
||||
[node name="Miniature" parent="Levels" instance=ExtResource("4_qjbly")]
|
||||
|
||||
[node name="AlignReference" parent="." instance=ExtResource("3_e1tcn")]
|
||||
visible = false
|
||||
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
|
||||
|
||||
const ConstructRoomMesh = preload ("res://lib/utils/mesh/construct_room_mesh.gd")
|
||||
|
||||
@onready var wall_corners = $Ceiling/WallCorners
|
||||
@onready var wall_edges = $Ceiling/WallEdges
|
||||
@onready var wall_mesh: MeshInstance3D = $WallMesh
|
||||
@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_ceiling = $Ceiling
|
||||
|
@ -55,75 +57,12 @@ func get_aabb():
|
|||
return AABB(to_global(min_pos), to_global(max_pos) - to_global(min_pos))
|
||||
|
||||
static func generate_wall_mesh(room_store: Dictionary):
|
||||
if room_store.corners.size() < 2:
|
||||
return null
|
||||
var corners = room_store.corners
|
||||
var height = room_store.height
|
||||
|
||||
var st = SurfaceTool.new()
|
||||
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
|
||||
return ConstructRoomMesh.generate_wall_mesh(corners, height)
|
||||
|
||||
static func generate_ceiling_mesh(room_store: Dictionary):
|
||||
|
||||
var points: PackedVector2Array = PackedVector2Array()
|
||||
var edges: PackedInt32Array = PackedInt32Array()
|
||||
var triangles: PackedInt32Array
|
||||
|
||||
var corners = room_store.corners
|
||||
|
||||
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()
|
||||
|
||||
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
|
||||
return ConstructRoomMesh.generate_ceiling_mesh(corners)
|
||||
|
|
|
@ -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/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://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/edit.gd" id="7_ap14h"]
|
||||
|
||||
|
@ -29,7 +28,11 @@ script = ExtResource("1_ugebq")
|
|||
|
||||
[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="."]
|
||||
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"]
|
||||
script = ExtResource("7_ap14h")
|
||||
|
||||
[node name="Mini" type="Node" parent="StateMachine"]
|
||||
script = ExtResource("6_g4qca")
|
||||
|
||||
[node name="Entities" type="Node3D" parent="."]
|
||||
|
|
|
@ -216,7 +216,6 @@ func add_corner(position: Vector3, index: int = -1):
|
|||
|
||||
moving.position=new_position
|
||||
|
||||
|
||||
if room.wall_edges.get_child_count() == 0:
|
||||
return
|
||||
|
||||
|
@ -247,7 +246,6 @@ func add_corner(position: Vector3, index: int = -1):
|
|||
else:
|
||||
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):
|
||||
var edge: StaticBody3D = wall_edge_scene.instantiate()
|
||||
edge.align_to_corners(from_pos, to_pos)
|
||||
|
@ -258,7 +256,6 @@ func add_edge(from_pos: Vector3, to_pos: Vector3, index: int = -1):
|
|||
add_corner(point, edge.get_index() + 1)
|
||||
)
|
||||
|
||||
|
||||
room.wall_edges.add_child(edge)
|
||||
room.wall_edges.move_child(edge, index)
|
||||
return edge
|
||||
|
@ -277,4 +274,7 @@ func update_store():
|
|||
store_room.corners = corners
|
||||
store_room.height = room.room_ceiling.position.y
|
||||
|
||||
# Manually update the array
|
||||
Store.house.state.rooms = Store.house.state.rooms
|
||||
|
||||
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,4 +1,4 @@
|
|||
extends State
|
||||
extends MachineState
|
||||
const Room = preload ("res://content/system/house/room/room.gd")
|
||||
|
||||
var room: Room
|
||||
|
|
|
@ -29,50 +29,12 @@ func _on_enter():
|
|||
floor_shape.shape = room.ceiling_mesh.mesh.create_trimesh_shape()
|
||||
ceiling_shape.shape.backface_collision = true
|
||||
|
||||
var collisions = generate_collision()
|
||||
|
||||
for collision in collisions:
|
||||
var static_body = StaticBody3D.new()
|
||||
static_body.set_collision_layer_value(4, true)
|
||||
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)
|
||||
var wall_collisions = room.wall_mesh.mesh.create_trimesh_shape()
|
||||
wall_collisions.backface_collision = true
|
||||
room.wall_collision.shape = wall_collisions
|
||||
|
||||
func _on_leave():
|
||||
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.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
|
||||
room.wall_collision.shape = null
|
||||
|
|
|
@ -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_up()
|
||||
signal on_toggled(active: bool)
|
||||
|
||||
const IconFont = preload ("res://assets/icons/icons.tres")
|
||||
const ECHO_WAIT_INITIAL = 0.5
|
||||
|
@ -57,6 +58,10 @@ const ECHO_WAIT_REPEAT = 0.1
|
|||
|
||||
var active: bool = false:
|
||||
set(value):
|
||||
if active == value:
|
||||
return
|
||||
|
||||
on_toggled.emit(value)
|
||||
active = value
|
||||
if !is_inside_tree(): return
|
||||
update_animation(1.0 if active else 0.0)
|
||||
|
|
|
@ -3,29 +3,29 @@ class_name Tabs3D
|
|||
|
||||
signal on_select(selected: int)
|
||||
|
||||
var selected: Node3D:
|
||||
set(value):
|
||||
if selected == value:
|
||||
return
|
||||
var selected = R.state(null)
|
||||
|
||||
if selected != null:
|
||||
selected.active = false
|
||||
|
||||
selected = value
|
||||
selected.active = true
|
||||
on_select.emit(selected.get_index())
|
||||
@export var initial_selected: Node3D
|
||||
|
||||
func _ready():
|
||||
if initial_selected != null:
|
||||
selected = initial_selected
|
||||
if initial_selected:
|
||||
selected.value = initial_selected
|
||||
|
||||
R.effect(func(_arg):
|
||||
on_select.emit(selected.value)
|
||||
)
|
||||
|
||||
for option in get_children():
|
||||
if option is Button3D == false:
|
||||
continue
|
||||
|
||||
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
|
||||
|
|
|
@ -8,15 +8,14 @@ var children: Array = []
|
|||
func _ready():
|
||||
children = get_children()
|
||||
|
||||
for child in children:
|
||||
for i in range(children.size()):
|
||||
var child = children[i]
|
||||
child.visible = true
|
||||
if tabs.selected != null && tabs.selected.get_index() == child.get_index():
|
||||
continue
|
||||
remove_child(child)
|
||||
|
||||
tabs.on_select.connect(func(index):
|
||||
for child in get_children():
|
||||
R.effect(func(_arg):
|
||||
if tabs.selected.value.get_index() == i:
|
||||
add_child(child)
|
||||
else:
|
||||
remove_child(child)
|
||||
|
||||
add_child(children[index])
|
||||
)
|
||||
|
|
|
@ -32,6 +32,15 @@ func _enter_tree():
|
|||
func load_devices():
|
||||
if devices.size() == 0:
|
||||
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()
|
||||
|
||||
HomeApi.on_disconnect.connect(func():
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
extends Node3D
|
||||
|
||||
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_unselected = preload ("../room_unselected.tres")
|
||||
|
@ -102,7 +102,7 @@ func _on_click(event: EventPointer):
|
|||
selected_room = room_name
|
||||
|
||||
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_offset = Vector2(0, 0.05)
|
||||
|
@ -128,7 +128,7 @@ func _generate_room_map():
|
|||
current_max.y = max(current_max.y, corner.y)
|
||||
|
||||
for room in rooms:
|
||||
var mesh = RoomType.generate_ceiling_mesh(room)
|
||||
var mesh = ConstructRoomMesh.generate_ceiling_mesh(room.corners)
|
||||
|
||||
if mesh == null:
|
||||
continue
|
||||
|
|
|
@ -3,7 +3,6 @@ extends Node3D
|
|||
const credits_scene = preload ("./credits.tscn")
|
||||
|
||||
@onready var connection_status = $Content/ConnectionStatus
|
||||
@onready var main = $"/root/Main"
|
||||
|
||||
@onready var input_url = $Content/InputURL
|
||||
@onready var input_token = $Content/InputToken
|
||||
|
@ -15,6 +14,8 @@ const credits_scene = preload ("./credits.tscn")
|
|||
@onready var voice_assist = $Content/VoiceAssist
|
||||
|
||||
func _ready():
|
||||
var settings_store = Store.settings.state
|
||||
|
||||
background.visible = false
|
||||
|
||||
credits.on_click.connect(func(_event):
|
||||
|
@ -25,12 +26,12 @@ func _ready():
|
|||
)
|
||||
|
||||
if Store.settings.is_loaded():
|
||||
input_url.text = Store.settings.url
|
||||
input_token.text = Store.settings.token
|
||||
input_url.text = settings_store.url
|
||||
input_token.text = settings_store.token
|
||||
else:
|
||||
Store.settings.on_loaded.connect(func():
|
||||
input_url.text=Store.settings.url
|
||||
input_token.text=Store.settings.token
|
||||
input_url.text=settings_store.url
|
||||
input_token.text=settings_store.token
|
||||
)
|
||||
|
||||
button_connect.on_button_down.connect(func():
|
||||
|
@ -39,8 +40,8 @@ func _ready():
|
|||
|
||||
HomeApi.start_adapter("hass_ws", url, token)
|
||||
|
||||
Store.settings.url=url
|
||||
Store.settings.token=token
|
||||
settings_store.url=url
|
||||
settings_store.token=token
|
||||
|
||||
Store.settings.save_local()
|
||||
)
|
||||
|
@ -63,8 +64,7 @@ func _ready():
|
|||
|
||||
voice_assist.label="mic"
|
||||
|
||||
Store.settings.voice_assistant=true
|
||||
main.update_voice_assistant()
|
||||
settings_store.voice_assistant=true
|
||||
Store.settings.save_local()
|
||||
)
|
||||
|
||||
|
@ -74,8 +74,7 @@ func _ready():
|
|||
|
||||
voice_assist.label="mic_off"
|
||||
|
||||
Store.settings.voice_assistant=false
|
||||
main.update_voice_assistant()
|
||||
settings_store.voice_assistant=false
|
||||
Store.settings.save_local()
|
||||
|
||||
)
|
||||
|
@ -91,5 +90,9 @@ func _ready():
|
|||
if Store.settings.is_loaded() == false:
|
||||
await Store.settings.on_loaded
|
||||
|
||||
voice_assist.label = "mic_off" if Store.settings.voice_assistant == false else "mic"
|
||||
voice_assist.active = Store.settings.voice_assistant
|
||||
var button_label = R.computed(func(_arg):
|
||||
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
|
||||
|
||||
@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
|
||||
|
||||
func _ready():
|
||||
background.visible = false
|
||||
|
||||
mini_view.on_button_down.connect(func():
|
||||
House.body.mini_view = !House.body.mini_view
|
||||
if !House.body.is_node_ready():
|
||||
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="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"]
|
||||
size = Vector3(0.3, 0.01, 0.3)
|
||||
|
@ -11,6 +13,7 @@ script = ExtResource("1_hxajx")
|
|||
|
||||
[node name="Background" type="MeshInstance3D" parent="."]
|
||||
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")
|
||||
|
||||
[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)
|
||||
label = "view_comfy"
|
||||
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:
|
||||
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()
|
||||
return
|
||||
|
||||
|
@ -24,7 +24,7 @@ func _ready():
|
|||
EventSystem.on_slow_tick.connect(_slow_tick)
|
||||
|
||||
func close():
|
||||
Store.settings.onboarding_complete = true
|
||||
Store.settings.state.onboarding_complete = true
|
||||
Store.settings.save_local()
|
||||
queue_free()
|
||||
|
||||
|
|
|
@ -33,7 +33,8 @@ func _ready():
|
|||
var success = Store.settings.load_local()
|
||||
|
||||
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
|
||||
func start_adapter(type: String, url: String, token: String):
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
extends Node
|
||||
## 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
|
||||
const StoreClass = preload ("./store.gd")
|
||||
|
||||
func _init():
|
||||
self.state = R.state({})
|
||||
|
||||
func clear():
|
||||
pass
|
|
@ -3,29 +3,31 @@ extends StoreClass
|
|||
|
||||
const StoreClass = preload ("./store.gd")
|
||||
|
||||
func _init():
|
||||
_save_path = "user://house.json"
|
||||
|
||||
self.state = R.store({
|
||||
## Type Room
|
||||
## name: String
|
||||
## corners: Vec2[]
|
||||
## height: float
|
||||
var rooms = []
|
||||
"rooms": [],
|
||||
## Type Entity
|
||||
## id: String
|
||||
## position: Vec3
|
||||
## rotation: Vec3
|
||||
## room: String
|
||||
var entities = []
|
||||
var align_position1: Vector3
|
||||
var align_position2: Vector3
|
||||
|
||||
func _init():
|
||||
_save_path = "user://house.json"
|
||||
"entities": [],
|
||||
"align_position1": Vector3(),
|
||||
"align_position2": Vector3()
|
||||
})
|
||||
|
||||
func clear():
|
||||
rooms = []
|
||||
entities = []
|
||||
self.state.rooms = []
|
||||
self.state.entities = []
|
||||
|
||||
func get_room(name):
|
||||
for room in rooms:
|
||||
for room in self.state.rooms:
|
||||
if room.name == name:
|
||||
return room
|
||||
return null
|
|
@ -3,23 +3,23 @@ extends StoreClass
|
|||
|
||||
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():
|
||||
_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():
|
||||
type = "HASS_WS"
|
||||
url = ""
|
||||
token = ""
|
||||
voice_assistant = false
|
||||
onboarding_complete = false
|
||||
self.state.type = "HASS_WS"
|
||||
self.state.url = ""
|
||||
self.state.token = ""
|
||||
self.state.voice_assistant = false
|
||||
self.state.onboarding_complete = false
|
||||
|
|
|
@ -9,6 +9,7 @@ signal on_loaded
|
|||
## Signal emitted when the data is saved.
|
||||
signal on_saved
|
||||
|
||||
var state: RdotStore
|
||||
var _loaded = false
|
||||
var _save_path = null
|
||||
|
||||
|
@ -20,44 +21,42 @@ func is_loaded():
|
|||
func clear():
|
||||
pass
|
||||
|
||||
func create_dict():
|
||||
var data: Dictionary = {}
|
||||
func sanitizeState(dict=state):
|
||||
var data = {}
|
||||
|
||||
for prop_info in get_property_list():
|
||||
if prop_info.name.begins_with("_")||prop_info.hint_string != "":
|
||||
for prop_info in state.get_property_list():
|
||||
var key = prop_info.name
|
||||
|
||||
if key.begins_with("_")||(prop_info.has("hint_string")&&prop_info.hint_string != ""):
|
||||
continue
|
||||
|
||||
var prop = get(prop_info.name)
|
||||
|
||||
if prop is Store:
|
||||
data[prop_info.name] = prop.create_dict()
|
||||
if dict[key] is Dictionary:
|
||||
data[key] = sanitizeState(dict[key])
|
||||
else:
|
||||
data[prop_info.name] = VariantSerializer.stringify_value(prop)
|
||||
data[key] = VariantSerializer.stringify_value(dict[key])
|
||||
|
||||
return data
|
||||
|
||||
func use_dict(dict: Dictionary):
|
||||
for prop_info in get_property_list():
|
||||
if prop_info.name.begins_with("_")||prop_info.hint_string != "":
|
||||
func use_dict(dict: Dictionary, target=state):
|
||||
for prop_info in state.get_property_list():
|
||||
var key = prop_info.name
|
||||
|
||||
if key.begins_with("_")||(prop_info.has("hint_string")&&prop_info.hint_string != ""):
|
||||
continue
|
||||
|
||||
var prop = get(prop_info.name)
|
||||
|
||||
if dict.has(prop_info.name) == false:
|
||||
if dict.has(key) == false:
|
||||
continue
|
||||
|
||||
var prop_value = dict[prop_info.name]
|
||||
|
||||
if prop is Store:
|
||||
prop.use_dict(prop_value)
|
||||
if target[key] is Dictionary:
|
||||
use_dict(dict[key], target[key])
|
||||
else:
|
||||
set(prop_info.name, prop_value)
|
||||
target[key] = dict[key]
|
||||
|
||||
func save_local(path=_save_path):
|
||||
if path == null:
|
||||
return false
|
||||
|
||||
var data = create_dict()
|
||||
var data = sanitizeState()
|
||||
|
||||
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
|
||||
## Base Class for all states
|
||||
class_name State
|
||||
class_name MachineState
|
||||
|
||||
var state_machine: StateMachine
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user