Compare commits
50 Commits
218-new-ui
...
arm64-dock
Author | SHA1 | Date | |
---|---|---|---|
6fc03ad0ee | |||
5a12b82392 | |||
35ee8bc8ec | |||
cef3cd445c | |||
2b205c4b62 | |||
ed4b26f925 | |||
c9758e6f11 | |||
7152ea9307 | |||
0f35031375 | |||
37b0b5040d | |||
318bd50eec | |||
fd733d45bf | |||
09af14bc4b | |||
e4e93f7c7f | |||
de32bd89e3 | |||
50e86aea15 | |||
c05a12343c | |||
a09193fba2 | |||
60e21f2b8e | |||
421f00879f | |||
964e6a8c63 | |||
80c81dc934 | |||
563fdb693d | |||
a64eea3cea | |||
03ad7c0066 | |||
d685573834 | |||
0681b53141 | |||
6f02b38b0e | |||
92c773dae9 | |||
f48ade6390 | |||
63c4501b88 | |||
06e21c86ce | |||
9d484b00e9 | |||
de9255f71c | |||
99528ea3e0 | |||
bb342951a5 | |||
438a79177b | |||
08fc49c40f | |||
d7e25b1192 | |||
1671422143 | |||
a9620c0752 | |||
583beaf6fe | |||
126d2338f2 | |||
24b0c0ed8a | |||
07fc1cf000 | |||
8e0131b3a8 | |||
912a2d524c | |||
82a5124d64 | |||
cca5bf903b | |||
4c0d4cb1c7 |
@ -1,13 +1,43 @@
|
|||||||
stages:
|
stages:
|
||||||
- test
|
# - test
|
||||||
- build
|
# - build-addon-zip
|
||||||
- deploy
|
# - build-amd64
|
||||||
- doc
|
- build-arm64
|
||||||
|
# - doc
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
include:
|
# include:
|
||||||
- local: .gitlab/ci/test.gitlab-ci.yml
|
# # - local: .gitlab/ci/test.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/build.gitlab-ci.yml
|
# # - local: .gitlab/ci/build-addon.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/deploy.gitlab-ci.yml
|
# - local: .gitlab/ci/build-image.gitlab-ci.yml
|
||||||
- local: .gitlab/ci/doc.gitlab-ci.yml
|
# # - local: .gitlab/ci/doc.gitlab-ci.yml
|
||||||
|
|
||||||
|
build-arm64:
|
||||||
|
stage: build-arm64
|
||||||
|
# needs: ["build-addon-zip"]
|
||||||
|
image: slumber/docker-python
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker:19.03.12-dind
|
||||||
|
before_script:
|
||||||
|
- apk add curl
|
||||||
|
- mkdir -p ~/.docker/cli-plugins
|
||||||
|
- curl -sSLo ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/$BUILDX_VERSION/buildx-$BUILDX_VERSION.linux-amd64
|
||||||
|
- chmod +x ~/.docker/cli-plugins/docker-buildx
|
||||||
|
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
- docker info
|
||||||
|
script:
|
||||||
|
- RP_VERSION="$(python scripts/get_replication_version.py)"
|
||||||
|
- VERSION="$(python scripts/get_addon_version.py)"
|
||||||
|
- echo "Building docker image with replication ${RP_VERSION}"
|
||||||
|
- docker buildx create arm64 --use
|
||||||
|
- docker buildx build --platform linux/arm64 --build-arg version={VERSION} --tag registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION}-arm64 ./scripts/docker_server
|
||||||
|
- echo "Pushing to gitlab registry ${VERSION}-arm64"
|
||||||
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
- docker tag registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION}-arm64 registry.gitlab.com/slumber/multi-user/multi-user-server:${CI_COMMIT_REF_NAME}-arm64
|
||||||
|
- docker push registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION}-arm64
|
@ -1,5 +1,5 @@
|
|||||||
build:
|
build-addon-zip:
|
||||||
stage: build
|
stage: build-addon-zip
|
||||||
needs: ["test"]
|
needs: ["test"]
|
||||||
image: debian:stable-slim
|
image: debian:stable-slim
|
||||||
script:
|
script:
|
50
.gitlab/ci/build-image.gitlab-ci.yml
Normal file
50
.gitlab/ci/build-image.gitlab-ci.yml
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
build-amd64:
|
||||||
|
stage: build-amd64
|
||||||
|
needs: ["build-addon-zip"]
|
||||||
|
image: slumber/docker-python
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker:19.03.12-dind
|
||||||
|
|
||||||
|
script:
|
||||||
|
- RP_VERSION="$(python scripts/get_replication_version.py)"
|
||||||
|
- VERSION="$(python scripts/get_addon_version.py)"
|
||||||
|
- echo "Building docker image with replication ${RP_VERSION}"
|
||||||
|
- docker build --build-arg replication_version=${RP_VERSION} --build-arg version={VERSION} -t registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION} ./scripts/docker_server
|
||||||
|
- echo "Pushing to gitlab registry ${VERSION}"
|
||||||
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
- docker tag registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION} registry.gitlab.com/slumber/multi-user/multi-user-server:${CI_COMMIT_REF_NAME}
|
||||||
|
- docker push registry.gitlab.com/slumber/multi-user/multi-user-server
|
||||||
|
|
||||||
|
build-arm64:
|
||||||
|
stage: build-arm64
|
||||||
|
# needs: ["build-addon-zip"]
|
||||||
|
image: slumber/docker-python
|
||||||
|
variables:
|
||||||
|
DOCKER_DRIVER: overlay2
|
||||||
|
DOCKER_TLS_CERTDIR: "/certs"
|
||||||
|
GIT_SUBMODULE_STRATEGY: recursive
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker:19.03.12-dind
|
||||||
|
before_script:
|
||||||
|
- apk add curl
|
||||||
|
- mkdir -p ~/.docker/cli-plugins
|
||||||
|
- curl -sSLo ~/.docker/cli-plugins/docker-buildx https://github.com/docker/buildx/releases/download/$BUILDX_VERSION/buildx-$BUILDX_VERSION.linux-amd64
|
||||||
|
- chmod +x ~/.docker/cli-plugins/docker-buildx
|
||||||
|
- docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||||
|
- docker info
|
||||||
|
script:
|
||||||
|
- RP_VERSION="$(python scripts/get_replication_version.py)"
|
||||||
|
- VERSION="$(python scripts/get_addon_version.py)"
|
||||||
|
- echo "Building docker image with replication ${RP_VERSION}"
|
||||||
|
- docker buildx create arm64 --use
|
||||||
|
- docker buildx build --platform linux/arm64 --build-arg version={VERSION} --tag registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION}-arm64 ./scripts/docker_server
|
||||||
|
- echo "Pushing to gitlab registry ${VERSION}-arm64"
|
||||||
|
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
||||||
|
- docker tag registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION}-arm64 registry.gitlab.com/slumber/multi-user/multi-user-server:${CI_COMMIT_REF_NAME}-arm64
|
||||||
|
- docker push registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION}-arm64
|
@ -1,21 +0,0 @@
|
|||||||
deploy:
|
|
||||||
stage: deploy
|
|
||||||
needs: ["build"]
|
|
||||||
image: slumber/docker-python
|
|
||||||
variables:
|
|
||||||
DOCKER_DRIVER: overlay2
|
|
||||||
DOCKER_TLS_CERTDIR: "/certs"
|
|
||||||
GIT_SUBMODULE_STRATEGY: recursive
|
|
||||||
|
|
||||||
services:
|
|
||||||
- docker:19.03.12-dind
|
|
||||||
|
|
||||||
script:
|
|
||||||
- RP_VERSION="$(python scripts/get_replication_version.py)"
|
|
||||||
- VERSION="$(python scripts/get_addon_version.py)"
|
|
||||||
- echo "Building docker image with replication ${RP_VERSION}"
|
|
||||||
- docker build --build-arg replication_version=${RP_VERSION} --build-arg version={VERSION} -t registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION} ./scripts/docker_server
|
|
||||||
- echo "Pushing to gitlab registry ${VERSION}"
|
|
||||||
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
|
|
||||||
- docker tag registry.gitlab.com/slumber/multi-user/multi-user-server:${VERSION} registry.gitlab.com/slumber/multi-user/multi-user-server:${CI_COMMIT_REF_NAME}
|
|
||||||
- docker push registry.gitlab.com/slumber/multi-user/multi-user-server
|
|
@ -1,6 +1,6 @@
|
|||||||
pages:
|
pages:
|
||||||
stage: doc
|
stage: doc
|
||||||
needs: ["deploy"]
|
needs: ["build-arm64","build-amd64"]
|
||||||
image: python
|
image: python
|
||||||
script:
|
script:
|
||||||
- pip install -U sphinx sphinx_rtd_theme sphinx-material
|
- pip install -U sphinx sphinx_rtd_theme sphinx-material
|
||||||
|
@ -212,14 +212,14 @@ You can run the dedicated server on any platform by following these steps:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
replication.server
|
replication.serve
|
||||||
|
|
||||||
.. hint::
|
.. hint::
|
||||||
You can also specify a custom **port** (-p), **timeout** (-t), **admin password** (-pwd), **log level (ERROR, WARNING, INFO or DEBUG)** (-l) and **log file** (-lf) with the following optional arguments
|
You can also specify a custom **port** (-p), **timeout** (-t), **admin password** (-pwd), **log level (ERROR, WARNING, INFO or DEBUG)** (-l) and **log file** (-lf) with the following optional arguments
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
replication.server -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
|
replication.serve -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
|
||||||
|
|
||||||
Here, for example, a server is instantiated on port 5555, with password 'admin', a 5 second timeout, and logging enabled.
|
Here, for example, a server is instantiated on port 5555, with password 'admin', a 5 second timeout, and logging enabled.
|
||||||
|
|
||||||
@ -562,7 +562,7 @@ The default Docker image essentially runs the equivalent of:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
replication.server -pwd admin -p 5555 -t 5000 -l DEBUG -lf multiuser_server.log
|
replication.serve -pwd admin -p 5555 -t 5000 -l DEBUG -lf multiuser_server.log
|
||||||
|
|
||||||
This means the server will be launched with 'admin' as the administrator password, run on ports 5555:5558, use a timeout of 5 seconds, verbose 'DEBUG' log level, and with log files written to 'multiuser_server.log'. See :ref:`cmd-line` for a description of optional parameters.
|
This means the server will be launched with 'admin' as the administrator password, run on ports 5555:5558, use a timeout of 5 seconds, verbose 'DEBUG' log level, and with log files written to 'multiuser_server.log'. See :ref:`cmd-line` for a description of optional parameters.
|
||||||
|
|
||||||
@ -572,7 +572,7 @@ For example, I would like to launch my server with a different administrator pas
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python3 -m replication.server -pwd supersecretpassword -p 5555 -t 3000 -l DEBUG -lf logname.log
|
replication.serve -pwd supersecretpassword -p 5555 -t 3000 -l DEBUG -lf logname.log
|
||||||
|
|
||||||
Now, my configuration should look like this:
|
Now, my configuration should look like this:
|
||||||
|
|
||||||
@ -691,7 +691,7 @@ We're finally ready to launch the server. Simply run:
|
|||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
python3 -m replication.server -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
|
replication.serve -p 5555 -pwd admin -t 5000 -l INFO -lf server.log
|
||||||
|
|
||||||
See :ref:`cmd-line` for a description of optional parameters
|
See :ref:`cmd-line` for a description of optional parameters
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ __all__ = [
|
|||||||
"bl_particle",
|
"bl_particle",
|
||||||
] # Order here defines execution order
|
] # Order here defines execution order
|
||||||
|
|
||||||
if bpy.app.version[1] >= 91:
|
if bpy.app.version >= (2,91,0):
|
||||||
__all__.append('bl_volume')
|
__all__.append('bl_volume')
|
||||||
|
|
||||||
from . import *
|
from . import *
|
||||||
|
@ -53,12 +53,12 @@ STROKE = [
|
|||||||
"uv_translation",
|
"uv_translation",
|
||||||
"vertex_color_fill",
|
"vertex_color_fill",
|
||||||
]
|
]
|
||||||
if bpy.app.version[1] >= 91:
|
if bpy.app.version >= (2,91,0):
|
||||||
STROKE.append('use_cyclic')
|
STROKE.append('use_cyclic')
|
||||||
else:
|
else:
|
||||||
STROKE.append('draw_cyclic')
|
STROKE.append('draw_cyclic')
|
||||||
|
|
||||||
if bpy.app.version[1] >= 83:
|
if bpy.app.version >= (2,83,0):
|
||||||
STROKE_POINT.append('vertex_color')
|
STROKE_POINT.append('vertex_color')
|
||||||
|
|
||||||
def dump_stroke(stroke):
|
def dump_stroke(stroke):
|
||||||
|
@ -37,7 +37,7 @@ class BlLightprobe(ReplicatedDatablock):
|
|||||||
def construct(data: dict) -> object:
|
def construct(data: dict) -> object:
|
||||||
type = 'CUBE' if data['type'] == 'CUBEMAP' else data['type']
|
type = 'CUBE' if data['type'] == 'CUBEMAP' else data['type']
|
||||||
# See https://developer.blender.org/D6396
|
# See https://developer.blender.org/D6396
|
||||||
if bpy.app.version[1] >= 83:
|
if bpy.app.version >= (2,83,0):
|
||||||
return bpy.data.lightprobes.new(data["name"], type)
|
return bpy.data.lightprobes.new(data["name"], type)
|
||||||
else:
|
else:
|
||||||
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||||
@ -49,7 +49,7 @@ class BlLightprobe(ReplicatedDatablock):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def dump(datablock: object) -> dict:
|
def dump(datablock: object) -> dict:
|
||||||
if bpy.app.version[1] < 83:
|
if bpy.app.version < (2,83,0):
|
||||||
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
logging.warning("Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||||
|
|
||||||
dumper = Dumper()
|
dumper = Dumper()
|
||||||
|
@ -48,7 +48,7 @@ SHAPEKEY_BLOCK_ATTR = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
if bpy.app.version[1] >= 93:
|
if bpy.app.version >= (2,93,0):
|
||||||
SUPPORTED_GEOMETRY_NODE_PARAMETERS = (int, str, float)
|
SUPPORTED_GEOMETRY_NODE_PARAMETERS = (int, str, float)
|
||||||
else:
|
else:
|
||||||
SUPPORTED_GEOMETRY_NODE_PARAMETERS = (int, str)
|
SUPPORTED_GEOMETRY_NODE_PARAMETERS = (int, str)
|
||||||
@ -56,14 +56,24 @@ else:
|
|||||||
blender 2.92.")
|
blender 2.92.")
|
||||||
|
|
||||||
|
|
||||||
def get_node_group_inputs(node_group):
|
def get_node_group_properties_identifiers(node_group):
|
||||||
inputs = []
|
props_ids = []
|
||||||
|
# Inputs
|
||||||
for inpt in node_group.inputs:
|
for inpt in node_group.inputs:
|
||||||
if inpt.type in IGNORED_SOCKETS:
|
if inpt.type in IGNORED_SOCKETS:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
inputs.append(inpt)
|
props_ids.append((inpt.identifier, inpt.type))
|
||||||
return inputs
|
|
||||||
|
if inpt.type in ['INT', 'VALUE', 'BOOLEAN', 'RGBA', 'VECTOR']:
|
||||||
|
props_ids.append((f"{inpt.identifier}_attribute_name",'STR'))
|
||||||
|
props_ids.append((f"{inpt.identifier}_use_attribute", 'BOOL'))
|
||||||
|
|
||||||
|
for outpt in node_group.outputs:
|
||||||
|
if outpt.type not in IGNORED_SOCKETS and outpt.type in ['INT', 'VALUE', 'BOOLEAN', 'RGBA', 'VECTOR']:
|
||||||
|
props_ids.append((f"{outpt.identifier}_attribute_name", 'STR'))
|
||||||
|
|
||||||
|
return props_ids
|
||||||
# return [inpt.identifer for inpt in node_group.inputs if inpt.type not in IGNORED_SOCKETS]
|
# return [inpt.identifer for inpt in node_group.inputs if inpt.type not in IGNORED_SOCKETS]
|
||||||
|
|
||||||
|
|
||||||
@ -122,29 +132,35 @@ def load_physics(dumped_settings: dict, target: bpy.types.Object):
|
|||||||
bpy.ops.rigidbody.constraint_remove({"object": target})
|
bpy.ops.rigidbody.constraint_remove({"object": target})
|
||||||
|
|
||||||
|
|
||||||
def dump_modifier_geometry_node_inputs(modifier: bpy.types.Modifier) -> list:
|
def dump_modifier_geometry_node_props(modifier: bpy.types.Modifier) -> list:
|
||||||
""" Dump geometry node modifier input properties
|
""" Dump geometry node modifier input properties
|
||||||
|
|
||||||
:arg modifier: geometry node modifier to dump
|
:arg modifier: geometry node modifier to dump
|
||||||
:type modifier: bpy.type.Modifier
|
:type modifier: bpy.type.Modifier
|
||||||
"""
|
"""
|
||||||
dumped_inputs = []
|
dumped_props = []
|
||||||
for inpt in get_node_group_inputs(modifier.node_group):
|
|
||||||
input_value = modifier[inpt.identifier]
|
for prop_value, prop_type in get_node_group_properties_identifiers(modifier.node_group):
|
||||||
|
try:
|
||||||
|
prop_value = modifier[prop_value]
|
||||||
|
except KeyError as e:
|
||||||
|
logging.error(f"fail to dump geomety node modifier property : {prop_value} ({e})")
|
||||||
|
else:
|
||||||
|
dump = None
|
||||||
|
if isinstance(prop_value, bpy.types.ID):
|
||||||
|
dump = prop_value.uuid
|
||||||
|
elif isinstance(prop_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS):
|
||||||
|
dump = prop_value
|
||||||
|
elif hasattr(prop_value, 'to_list'):
|
||||||
|
dump = prop_value.to_list()
|
||||||
|
|
||||||
dumped_input = None
|
dumped_props.append((dump, prop_type))
|
||||||
if isinstance(input_value, bpy.types.ID):
|
# logging.info(prop_value)
|
||||||
dumped_input = input_value.uuid
|
|
||||||
elif isinstance(input_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS):
|
|
||||||
dumped_input = input_value
|
|
||||||
elif hasattr(input_value, 'to_list'):
|
|
||||||
dumped_input = input_value.to_list()
|
|
||||||
dumped_inputs.append(dumped_input)
|
|
||||||
|
|
||||||
return dumped_inputs
|
return dumped_props
|
||||||
|
|
||||||
|
|
||||||
def load_modifier_geometry_node_inputs(dumped_modifier: dict, target_modifier: bpy.types.Modifier):
|
def load_modifier_geometry_node_props(dumped_modifier: dict, target_modifier: bpy.types.Modifier):
|
||||||
""" Load geometry node modifier inputs
|
""" Load geometry node modifier inputs
|
||||||
|
|
||||||
:arg dumped_modifier: source dumped modifier to load
|
:arg dumped_modifier: source dumped modifier to load
|
||||||
@ -153,17 +169,17 @@ def load_modifier_geometry_node_inputs(dumped_modifier: dict, target_modifier: b
|
|||||||
:type target_modifier: bpy.type.Modifier
|
:type target_modifier: bpy.type.Modifier
|
||||||
"""
|
"""
|
||||||
|
|
||||||
for input_index, inpt in enumerate(get_node_group_inputs(target_modifier.node_group)):
|
for input_index, inpt in enumerate(get_node_group_properties_identifiers(target_modifier.node_group)):
|
||||||
dumped_value = dumped_modifier['inputs'][input_index]
|
dumped_value, dumped_type = dumped_modifier['props'][input_index]
|
||||||
input_value = target_modifier[inpt.identifier]
|
input_value = target_modifier[inpt[0]]
|
||||||
if isinstance(input_value, SUPPORTED_GEOMETRY_NODE_PARAMETERS):
|
if dumped_type in ['INT', 'VALUE', 'STR']:
|
||||||
target_modifier[inpt.identifier] = dumped_value
|
logging.info(f"{inpt[0]}/{dumped_value}")
|
||||||
elif hasattr(input_value, 'to_list'):
|
target_modifier[inpt[0]] = dumped_value
|
||||||
|
elif dumped_type in ['RGBA', 'VECTOR']:
|
||||||
for index in range(len(input_value)):
|
for index in range(len(input_value)):
|
||||||
input_value[index] = dumped_value[index]
|
input_value[index] = dumped_value[index]
|
||||||
elif inpt.type in ['COLLECTION', 'OBJECT']:
|
elif dumped_type in ['COLLECTION', 'OBJECT', 'IMAGE', 'TEXTURE', 'MATERIAL']:
|
||||||
target_modifier[inpt.identifier] = get_datablock_from_uuid(
|
target_modifier[inpt[0]] = get_datablock_from_uuid(dumped_value, None)
|
||||||
dumped_value, None)
|
|
||||||
|
|
||||||
|
|
||||||
def load_pose(target_bone, data):
|
def load_pose(target_bone, data):
|
||||||
@ -198,12 +214,12 @@ def find_data_from_name(name=None):
|
|||||||
instance = bpy.data.speakers[name]
|
instance = bpy.data.speakers[name]
|
||||||
elif name in bpy.data.lightprobes.keys():
|
elif name in bpy.data.lightprobes.keys():
|
||||||
# Only supported since 2.83
|
# Only supported since 2.83
|
||||||
if bpy.app.version[1] >= 83:
|
if bpy.app.version >= (2,83,0):
|
||||||
instance = bpy.data.lightprobes[name]
|
instance = bpy.data.lightprobes[name]
|
||||||
else:
|
else:
|
||||||
logging.warning(
|
logging.warning(
|
||||||
"Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
"Lightprobe replication only supported since 2.83. See https://developer.blender.org/D6396")
|
||||||
elif bpy.app.version[1] >= 91 and name in bpy.data.volumes.keys():
|
elif bpy.app.version >= (2,91,0) and name in bpy.data.volumes.keys():
|
||||||
# Only supported since 2.91
|
# Only supported since 2.91
|
||||||
instance = bpy.data.volumes[name]
|
instance = bpy.data.volumes[name]
|
||||||
return instance
|
return instance
|
||||||
@ -250,10 +266,11 @@ def find_geometry_nodes_dependencies(modifiers: bpy.types.bpy_prop_collection) -
|
|||||||
for mod in modifiers:
|
for mod in modifiers:
|
||||||
if mod.type == 'NODES' and mod.node_group:
|
if mod.type == 'NODES' and mod.node_group:
|
||||||
dependencies.append(mod.node_group)
|
dependencies.append(mod.node_group)
|
||||||
# for inpt in get_node_group_inputs(mod.node_group):
|
for inpt, inpt_type in get_node_group_properties_identifiers(mod.node_group):
|
||||||
# parameter = mod.get(inpt.identifier)
|
inpt_value = mod.get(inpt)
|
||||||
# if parameter and isinstance(parameter, bpy.types.ID):
|
# Avoid to handle 'COLLECTION', 'OBJECT' to avoid circular dependencies
|
||||||
# dependencies.append(parameter)
|
if inpt_type in ['IMAGE', 'TEXTURE', 'MATERIAL'] and inpt_value:
|
||||||
|
dependencies.append(inpt_value)
|
||||||
|
|
||||||
return dependencies
|
return dependencies
|
||||||
|
|
||||||
@ -387,10 +404,7 @@ def dump_modifiers(modifiers: bpy.types.bpy_prop_collection)->dict:
|
|||||||
dumped_modifier = dumper.dump(modifier)
|
dumped_modifier = dumper.dump(modifier)
|
||||||
# hack to dump geometry nodes inputs
|
# hack to dump geometry nodes inputs
|
||||||
if modifier.type == 'NODES':
|
if modifier.type == 'NODES':
|
||||||
dumped_inputs = dump_modifier_geometry_node_inputs(
|
dumped_modifier['props'] = dump_modifier_geometry_node_props(modifier)
|
||||||
modifier)
|
|
||||||
dumped_modifier['inputs'] = dumped_inputs
|
|
||||||
|
|
||||||
elif modifier.type == 'PARTICLE_SYSTEM':
|
elif modifier.type == 'PARTICLE_SYSTEM':
|
||||||
dumper.exclude_filter = [
|
dumper.exclude_filter = [
|
||||||
"is_edited",
|
"is_edited",
|
||||||
@ -455,7 +469,7 @@ def load_modifiers(dumped_modifiers: list, modifiers: bpy.types.bpy_prop_collect
|
|||||||
loader.load(loaded_modifier, dumped_modifier)
|
loader.load(loaded_modifier, dumped_modifier)
|
||||||
|
|
||||||
if loaded_modifier.type == 'NODES':
|
if loaded_modifier.type == 'NODES':
|
||||||
load_modifier_geometry_node_inputs(dumped_modifier, loaded_modifier)
|
load_modifier_geometry_node_props(dumped_modifier, loaded_modifier)
|
||||||
elif loaded_modifier.type == 'PARTICLE_SYSTEM':
|
elif loaded_modifier.type == 'PARTICLE_SYSTEM':
|
||||||
default = loaded_modifier.particle_system.settings
|
default = loaded_modifier.particle_system.settings
|
||||||
dumped_particles = dumped_modifier['particle_system']
|
dumped_particles = dumped_modifier['particle_system']
|
||||||
|
@ -440,7 +440,7 @@ class BlScene(ReplicatedDatablock):
|
|||||||
if seq.name not in sequences:
|
if seq.name not in sequences:
|
||||||
vse.sequences.remove(seq)
|
vse.sequences.remove(seq)
|
||||||
# Load existing sequences
|
# Load existing sequences
|
||||||
for seq_data in sequences.value():
|
for seq_data in sequences.values():
|
||||||
load_sequence(seq_data, vse)
|
load_sequence(seq_data, vse)
|
||||||
# If the sequence is no longer used, clear it
|
# If the sequence is no longer used, clear it
|
||||||
elif datablock.sequence_editor and not sequences:
|
elif datablock.sequence_editor and not sequences:
|
||||||
|
@ -134,7 +134,7 @@ def install_modules(dependencies: list, python_path: str, install_dir: str):
|
|||||||
module_can_be_imported(package_name)
|
module_can_be_imported(package_name)
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
if bpy.app.version[1] >= 91:
|
if bpy.app.version >= (2,91,0):
|
||||||
python_binary_path = sys.executable
|
python_binary_path = sys.executable
|
||||||
else:
|
else:
|
||||||
python_binary_path = bpy.app.binary_path_python
|
python_binary_path = bpy.app.binary_path_python
|
||||||
|
Submodule multi_user/libs/replication updated: d283d39e9c...9aa015bd69
@ -37,6 +37,7 @@ from queue import Queue
|
|||||||
from time import gmtime, strftime
|
from time import gmtime, strftime
|
||||||
|
|
||||||
from bpy.props import FloatProperty
|
from bpy.props import FloatProperty
|
||||||
|
import bmesh
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import _pickle as pickle
|
import _pickle as pickle
|
||||||
@ -58,13 +59,126 @@ from replication.repository import Repository
|
|||||||
|
|
||||||
from . import bl_types, environment, shared_data, timers, ui, utils
|
from . import bl_types, environment, shared_data, timers, ui, utils
|
||||||
from .handlers import on_scene_update, sanitize_deps_graph
|
from .handlers import on_scene_update, sanitize_deps_graph
|
||||||
from .presence import SessionStatusWidget, renderer, view3d_find, refresh_sidebar_view
|
from .presence import SessionStatusWidget, renderer, view3d_find, refresh_sidebar_view, bbox_from_obj
|
||||||
from .timers import registry
|
from .timers import registry
|
||||||
|
|
||||||
background_execution_queue = Queue()
|
background_execution_queue = Queue()
|
||||||
deleyables = []
|
deleyables = []
|
||||||
stop_modal_executor = False
|
stop_modal_executor = False
|
||||||
|
|
||||||
|
|
||||||
|
def draw_user(username, metadata, radius=0.01, intensity=10.0):
|
||||||
|
view_corners = metadata.get('view_corners')
|
||||||
|
color = metadata.get('color', (1,1,1,0))
|
||||||
|
objects = metadata.get('selected_objects', None)
|
||||||
|
|
||||||
|
user_collection = bpy.data.collections.new(username)
|
||||||
|
|
||||||
|
# User Color
|
||||||
|
user_mat = bpy.data.materials.new(username)
|
||||||
|
user_mat.use_nodes = True
|
||||||
|
nodes = user_mat.node_tree.nodes
|
||||||
|
nodes.remove(nodes['Principled BSDF'])
|
||||||
|
emission_node = nodes.new('ShaderNodeEmission')
|
||||||
|
emission_node.inputs['Color'].default_value = color
|
||||||
|
emission_node.inputs['Strength'].default_value = intensity
|
||||||
|
|
||||||
|
output_node = nodes['Material Output']
|
||||||
|
user_mat.node_tree.links.new(
|
||||||
|
emission_node.outputs['Emission'], output_node.inputs['Surface'])
|
||||||
|
|
||||||
|
# Generate camera mesh
|
||||||
|
camera_vertices = view_corners[:4]
|
||||||
|
camera_vertices.append(view_corners[6])
|
||||||
|
camera_mesh = bpy.data.meshes.new(f"{username}_camera")
|
||||||
|
camera_obj = bpy.data.objects.new(f"{username}_camera", camera_mesh)
|
||||||
|
frustum_bm = bmesh.new()
|
||||||
|
frustum_bm.from_mesh(camera_mesh)
|
||||||
|
|
||||||
|
for p in camera_vertices:
|
||||||
|
frustum_bm.verts.new(p)
|
||||||
|
frustum_bm.verts.ensure_lookup_table()
|
||||||
|
|
||||||
|
frustum_bm.edges.new((frustum_bm.verts[0], frustum_bm.verts[2]))
|
||||||
|
frustum_bm.edges.new((frustum_bm.verts[2], frustum_bm.verts[1]))
|
||||||
|
frustum_bm.edges.new((frustum_bm.verts[1], frustum_bm.verts[3]))
|
||||||
|
frustum_bm.edges.new((frustum_bm.verts[3], frustum_bm.verts[0]))
|
||||||
|
|
||||||
|
frustum_bm.edges.new((frustum_bm.verts[0], frustum_bm.verts[4]))
|
||||||
|
frustum_bm.edges.new((frustum_bm.verts[2], frustum_bm.verts[4]))
|
||||||
|
frustum_bm.edges.new((frustum_bm.verts[1], frustum_bm.verts[4]))
|
||||||
|
frustum_bm.edges.new((frustum_bm.verts[3], frustum_bm.verts[4]))
|
||||||
|
frustum_bm.edges.ensure_lookup_table()
|
||||||
|
|
||||||
|
frustum_bm.to_mesh(camera_mesh)
|
||||||
|
frustum_bm.free() # free and prevent further access
|
||||||
|
|
||||||
|
camera_obj.modifiers.new("wireframe", "SKIN")
|
||||||
|
camera_obj.data.skin_vertices[0].data[0].use_root = True
|
||||||
|
for v in camera_mesh.skin_vertices[0].data:
|
||||||
|
v.radius = [radius, radius]
|
||||||
|
|
||||||
|
camera_mesh.materials.append(user_mat)
|
||||||
|
user_collection.objects.link(camera_obj)
|
||||||
|
|
||||||
|
# Generate sight mesh
|
||||||
|
sight_mesh = bpy.data.meshes.new(f"{username}_sight")
|
||||||
|
sight_obj = bpy.data.objects.new(f"{username}_sight", sight_mesh)
|
||||||
|
sight_verts = view_corners[4:6]
|
||||||
|
sight_bm = bmesh.new()
|
||||||
|
sight_bm.from_mesh(sight_mesh)
|
||||||
|
|
||||||
|
for p in sight_verts:
|
||||||
|
sight_bm.verts.new(p)
|
||||||
|
sight_bm.verts.ensure_lookup_table()
|
||||||
|
|
||||||
|
sight_bm.edges.new((sight_bm.verts[0], sight_bm.verts[1]))
|
||||||
|
sight_bm.edges.ensure_lookup_table()
|
||||||
|
sight_bm.to_mesh(sight_mesh)
|
||||||
|
sight_bm.free()
|
||||||
|
|
||||||
|
sight_obj.modifiers.new("wireframe", "SKIN")
|
||||||
|
sight_obj.data.skin_vertices[0].data[0].use_root = True
|
||||||
|
for v in sight_mesh.skin_vertices[0].data:
|
||||||
|
v.radius = [radius, radius]
|
||||||
|
|
||||||
|
sight_mesh.materials.append(user_mat)
|
||||||
|
user_collection.objects.link(sight_obj)
|
||||||
|
|
||||||
|
# Draw selected objects
|
||||||
|
if objects:
|
||||||
|
for o in list(objects):
|
||||||
|
instance = bl_types.bl_datablock.get_datablock_from_uuid(o, None)
|
||||||
|
if instance:
|
||||||
|
bbox_mesh = bpy.data.meshes.new(f"{instance.name}_bbox")
|
||||||
|
bbox_obj = bpy.data.objects.new(
|
||||||
|
f"{instance.name}_bbox", bbox_mesh)
|
||||||
|
bbox_verts, bbox_ind = bbox_from_obj(instance, index=0)
|
||||||
|
bbox_bm = bmesh.new()
|
||||||
|
bbox_bm.from_mesh(bbox_mesh)
|
||||||
|
|
||||||
|
for p in bbox_verts:
|
||||||
|
bbox_bm.verts.new(p)
|
||||||
|
bbox_bm.verts.ensure_lookup_table()
|
||||||
|
|
||||||
|
for e in bbox_ind:
|
||||||
|
bbox_bm.edges.new(
|
||||||
|
(bbox_bm.verts[e[0]], bbox_bm.verts[e[1]]))
|
||||||
|
|
||||||
|
bbox_bm.to_mesh(bbox_mesh)
|
||||||
|
bbox_bm.free()
|
||||||
|
bpy.data.collections[username].objects.link(bbox_obj)
|
||||||
|
|
||||||
|
bbox_obj.modifiers.new("wireframe", "SKIN")
|
||||||
|
bbox_obj.data.skin_vertices[0].data[0].use_root = True
|
||||||
|
for v in bbox_mesh.skin_vertices[0].data:
|
||||||
|
v.radius = [radius, radius]
|
||||||
|
|
||||||
|
bbox_mesh.materials.append(user_mat)
|
||||||
|
|
||||||
|
bpy.context.scene.collection.children.link(user_collection)
|
||||||
|
|
||||||
|
|
||||||
def session_callback(name):
|
def session_callback(name):
|
||||||
""" Session callback wrapper
|
""" Session callback wrapper
|
||||||
|
|
||||||
@ -218,9 +332,9 @@ class SessionConnectOperator(bpy.types.Operator):
|
|||||||
|
|
||||||
settings = utils.get_preferences()
|
settings = utils.get_preferences()
|
||||||
users = bpy.data.window_managers['WinMan'].online_users
|
users = bpy.data.window_managers['WinMan'].online_users
|
||||||
active_server = get_active_server_preset()
|
active_server = get_active_server_preset(context)
|
||||||
admin_pass = active_server.admin_password if active_server.use_admin_password else None
|
admin_pass = active_server.admin_password if active_server.use_admin_password else None
|
||||||
server_pass = active_server.server_password if active_server.use_server_password else None
|
server_pass = active_server.server_password if active_server.use_server_password else ''
|
||||||
|
|
||||||
users.clear()
|
users.clear()
|
||||||
deleyables.clear()
|
deleyables.clear()
|
||||||
@ -238,7 +352,7 @@ class SessionConnectOperator(bpy.types.Operator):
|
|||||||
settings.generate_supported_types()
|
settings.generate_supported_types()
|
||||||
|
|
||||||
|
|
||||||
if bpy.app.version[1] >= 91:
|
if bpy.app.version >= (2,91,0):
|
||||||
python_binary_path = sys.executable
|
python_binary_path = sys.executable
|
||||||
else:
|
else:
|
||||||
python_binary_path = bpy.app.binary_path_python
|
python_binary_path = bpy.app.binary_path_python
|
||||||
@ -291,7 +405,7 @@ class SessionHostOperator(bpy.types.Operator):
|
|||||||
runtime_settings = context.window_manager.session
|
runtime_settings = context.window_manager.session
|
||||||
users = bpy.data.window_managers['WinMan'].online_users
|
users = bpy.data.window_managers['WinMan'].online_users
|
||||||
admin_pass = settings.host_admin_password if settings.host_use_admin_password else None
|
admin_pass = settings.host_admin_password if settings.host_use_admin_password else None
|
||||||
server_pass = settings.host_server_password if settings.host_use_server_password else None
|
server_pass = settings.host_server_password if settings.host_use_server_password else ''
|
||||||
|
|
||||||
users.clear()
|
users.clear()
|
||||||
deleyables.clear()
|
deleyables.clear()
|
||||||
@ -309,7 +423,7 @@ class SessionHostOperator(bpy.types.Operator):
|
|||||||
settings.generate_supported_types()
|
settings.generate_supported_types()
|
||||||
|
|
||||||
|
|
||||||
if bpy.app.version[1] >= 91:
|
if bpy.app.version >= (2,91,0):
|
||||||
python_binary_path = sys.executable
|
python_binary_path = sys.executable
|
||||||
else:
|
else:
|
||||||
python_binary_path = bpy.app.binary_path_python
|
python_binary_path = bpy.app.binary_path_python
|
||||||
@ -863,9 +977,28 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
|
|||||||
maxlen=255, # Max internal buffer length, longer would be clamped.
|
maxlen=255, # Max internal buffer length, longer would be clamped.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
draw_users: bpy.props.BoolProperty(
|
||||||
|
name="Load users",
|
||||||
|
description="Draw users in the scene",
|
||||||
|
default=False,
|
||||||
|
)
|
||||||
|
user_skin_radius: bpy.props.FloatProperty(
|
||||||
|
name="Wireframe radius",
|
||||||
|
description="Wireframe radius",
|
||||||
|
default=0.005,
|
||||||
|
)
|
||||||
|
user_color_intensity: bpy.props.FloatProperty(
|
||||||
|
name="Shading intensity",
|
||||||
|
description="Shading intensity",
|
||||||
|
default=10.0,
|
||||||
|
)
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
pass
|
||||||
|
|
||||||
def execute(self, context):
|
def execute(self, context):
|
||||||
from replication.repository import Repository
|
from replication.repository import Repository
|
||||||
|
|
||||||
# init the factory with supported types
|
# init the factory with supported types
|
||||||
bpy_protocol = bl_types.get_data_translation_protocol()
|
bpy_protocol = bl_types.get_data_translation_protocol()
|
||||||
repo = Repository(bpy_protocol)
|
repo = Repository(bpy_protocol)
|
||||||
@ -884,14 +1017,58 @@ class SessionLoadSaveOperator(bpy.types.Operator, ImportHelper):
|
|||||||
# Step 2: Load nodes
|
# Step 2: Load nodes
|
||||||
for node in nodes:
|
for node in nodes:
|
||||||
porcelain.apply(repo, node.uuid)
|
porcelain.apply(repo, node.uuid)
|
||||||
|
|
||||||
|
if self.draw_users:
|
||||||
|
f = gzip.open(self.filepath, "rb")
|
||||||
|
db = pickle.load(f)
|
||||||
|
|
||||||
|
users = db.get("users")
|
||||||
|
|
||||||
|
for username, user_data in users.items():
|
||||||
|
metadata = user_data['metadata']
|
||||||
|
|
||||||
|
if metadata:
|
||||||
|
draw_user(username, metadata, radius=self.user_skin_radius, intensity=self.user_color_intensity)
|
||||||
|
|
||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def poll(cls, context):
|
def poll(cls, context):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
class SessionImportUser(bpy.types.Panel):
|
||||||
|
bl_space_type = 'FILE_BROWSER'
|
||||||
|
bl_region_type = 'TOOL_PROPS'
|
||||||
|
bl_label = "Users"
|
||||||
|
bl_parent_id = "FILE_PT_operator"
|
||||||
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
sfile = context.space_data
|
||||||
|
operator = sfile.active_operator
|
||||||
|
|
||||||
|
return operator.bl_idname == "SESSION_OT_load"
|
||||||
|
|
||||||
|
def draw_header(self, context):
|
||||||
|
sfile = context.space_data
|
||||||
|
operator = sfile.active_operator
|
||||||
|
|
||||||
|
self.layout.prop(operator, "draw_users", text="")
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
layout.use_property_split = True
|
||||||
|
layout.use_property_decorate = False # No animation.
|
||||||
|
|
||||||
|
sfile = context.space_data
|
||||||
|
operator = sfile.active_operator
|
||||||
|
|
||||||
|
layout.enabled = operator.draw_users
|
||||||
|
|
||||||
|
layout.prop(operator, "user_skin_radius")
|
||||||
|
layout.prop(operator, "user_color_intensity")
|
||||||
|
|
||||||
class SessionPresetServerAdd(bpy.types.Operator):
|
class SessionPresetServerAdd(bpy.types.Operator):
|
||||||
"""Add a server to the server list preset"""
|
"""Add a server to the server list preset"""
|
||||||
bl_idname = "session.preset_server_add"
|
bl_idname = "session.preset_server_add"
|
||||||
@ -1123,6 +1300,7 @@ classes = (
|
|||||||
SessionNotifyOperator,
|
SessionNotifyOperator,
|
||||||
SessionSaveBackupOperator,
|
SessionSaveBackupOperator,
|
||||||
SessionLoadSaveOperator,
|
SessionLoadSaveOperator,
|
||||||
|
SessionImportUser,
|
||||||
SessionStopAutoSaveOperator,
|
SessionStopAutoSaveOperator,
|
||||||
SessionPurgeOperator,
|
SessionPurgeOperator,
|
||||||
SessionPresetServerAdd,
|
SessionPresetServerAdd,
|
||||||
|
@ -374,9 +374,9 @@ class SessionPrefs(bpy.types.AddonPreferences):
|
|||||||
description="sidebar_advanced_log_expanded",
|
description="sidebar_advanced_log_expanded",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
sidebar_advanced_hosting_expanded: bpy.props.BoolProperty(
|
sidebar_advanced_uinfo_expanded: bpy.props.BoolProperty(
|
||||||
name="sidebar_advanced_hosting_expanded",
|
name="sidebar_advanced_uinfo_expanded",
|
||||||
description="sidebar_advanced_hosting_expanded",
|
description="sidebar_advanced_uinfo_expanded",
|
||||||
default=False
|
default=False
|
||||||
)
|
)
|
||||||
sidebar_advanced_net_expanded: bpy.props.BoolProperty(
|
sidebar_advanced_net_expanded: bpy.props.BoolProperty(
|
||||||
|
@ -203,10 +203,11 @@ class DynamicRightSelectTimer(Timer):
|
|||||||
|
|
||||||
for node_id in to_lock:
|
for node_id in to_lock:
|
||||||
node = session.repository.graph.get(node_id)
|
node = session.repository.graph.get(node_id)
|
||||||
instance_mode = node.data.get('instance_type')
|
if node and hasattr(node,'data'):
|
||||||
if instance_mode and instance_mode == 'COLLECTION':
|
instance_mode = node.data.get('instance_type')
|
||||||
to_lock.remove(node_id)
|
if instance_mode and instance_mode == 'COLLECTION':
|
||||||
instances_to_lock.append(node_id)
|
to_lock.remove(node_id)
|
||||||
|
instances_to_lock.append(node_id)
|
||||||
if instances_to_lock:
|
if instances_to_lock:
|
||||||
try:
|
try:
|
||||||
porcelain.lock(session.repository,
|
porcelain.lock(session.repository,
|
||||||
|
117
multi_user/ui.py
117
multi_user/ui.py
@ -149,10 +149,8 @@ class SESSION_PT_settings(bpy.types.Panel):
|
|||||||
col.template_list("SESSION_UL_network", "", settings, "server_preset", context.window_manager, "server_index")
|
col.template_list("SESSION_UL_network", "", settings, "server_preset", context.window_manager, "server_index")
|
||||||
col.separator()
|
col.separator()
|
||||||
connectOp = col.row()
|
connectOp = col.row()
|
||||||
connectOp.operator("session.host", text="Host")
|
connectOp.enabled =is_server_selected
|
||||||
connectopcol = connectOp.column()
|
connectOp.operator("session.connect", text="Connect")
|
||||||
connectopcol.enabled =is_server_selected
|
|
||||||
connectopcol.operator("session.connect", text="Connect")
|
|
||||||
|
|
||||||
col = row.column(align=True)
|
col = row.column(align=True)
|
||||||
col.operator("session.preset_server_add", icon="ADD", text="") # TODO : add conditions (need a name, etc..)
|
col.operator("session.preset_server_add", icon="ADD", text="") # TODO : add conditions (need a name, etc..)
|
||||||
@ -173,12 +171,18 @@ class SESSION_PT_settings(bpy.types.Panel):
|
|||||||
info_msg = None
|
info_msg = None
|
||||||
|
|
||||||
if current_state == STATE_LOBBY:
|
if current_state == STATE_LOBBY:
|
||||||
|
usr = session.online_users.get(settings.username)
|
||||||
row= layout.row()
|
row= layout.row()
|
||||||
info_msg = "Waiting for the session to start."
|
info_msg = "Waiting for the session to start."
|
||||||
|
if usr and usr['admin']:
|
||||||
if info_msg:
|
info_msg = "Init the session to start."
|
||||||
info_box = row.box()
|
info_box = layout.row()
|
||||||
info_box.row().label(text=info_msg,icon='INFO')
|
info_box.label(text=info_msg,icon='INFO')
|
||||||
|
init_row = layout.row()
|
||||||
|
init_row.operator("session.init", icon='TOOL_SETTINGS', text="Init")
|
||||||
|
else:
|
||||||
|
info_box = layout.row()
|
||||||
|
info_box.row().label(text=info_msg,icon='INFO')
|
||||||
|
|
||||||
# PROGRESS BAR
|
# PROGRESS BAR
|
||||||
if current_state in [STATE_SYNCING, STATE_SRV_SYNC, STATE_WAITING]:
|
if current_state in [STATE_SYNCING, STATE_SRV_SYNC, STATE_WAITING]:
|
||||||
@ -192,10 +196,57 @@ class SESSION_PT_settings(bpy.types.Panel):
|
|||||||
length=16
|
length=16
|
||||||
))
|
))
|
||||||
|
|
||||||
|
class SESSION_PT_host_settings(bpy.types.Panel):
|
||||||
|
bl_idname = "MULTIUSER_SETTINGS_HOST_PT_panel"
|
||||||
|
bl_label = "Hosting"
|
||||||
|
bl_space_type = 'VIEW_3D'
|
||||||
|
bl_region_type = 'UI'
|
||||||
|
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||||
|
bl_options = {'DEFAULT_CLOSED'}
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def poll(cls, context):
|
||||||
|
settings = get_preferences()
|
||||||
|
return not session \
|
||||||
|
or (session and session.state == 0) \
|
||||||
|
and not settings.sidebar_advanced_shown \
|
||||||
|
and not settings.is_first_launch
|
||||||
|
|
||||||
|
def draw_header(self, context):
|
||||||
|
self.layout.label(text="", icon='NETWORK_DRIVE')
|
||||||
|
|
||||||
|
def draw(self, context):
|
||||||
|
layout = self.layout
|
||||||
|
settings = get_preferences()
|
||||||
|
|
||||||
|
#HOST
|
||||||
|
host_selection = layout.row().box()
|
||||||
|
host_selection_row = host_selection.row()
|
||||||
|
host_selection_row.label(text="Init the session from:")
|
||||||
|
host_selection_row.prop(settings, "init_method", text="")
|
||||||
|
host_selection_row = host_selection.row()
|
||||||
|
host_selection_row.label(text="Port:")
|
||||||
|
host_selection_row.prop(settings, "host_port", text="")
|
||||||
|
host_selection_row = host_selection.row()
|
||||||
|
host_selection_col = host_selection_row.column()
|
||||||
|
host_selection_col.prop(settings, "host_use_server_password", text="Server password:")
|
||||||
|
host_selection_col = host_selection_row.column()
|
||||||
|
host_selection_col.enabled = True if settings.host_use_server_password else False
|
||||||
|
host_selection_col.prop(settings, "host_server_password", text="")
|
||||||
|
host_selection_row = host_selection.row()
|
||||||
|
host_selection_col = host_selection_row.column()
|
||||||
|
host_selection_col.prop(settings, "host_use_admin_password", text="Admin password:")
|
||||||
|
host_selection_col = host_selection_row.column()
|
||||||
|
host_selection_col.enabled = True if settings.host_use_admin_password else False
|
||||||
|
host_selection_col.prop(settings, "host_admin_password", text="")
|
||||||
|
|
||||||
|
host_selection = layout.column()
|
||||||
|
host_selection.operator("session.host", text="Host")
|
||||||
|
|
||||||
|
|
||||||
class SESSION_PT_advanced_settings(bpy.types.Panel):
|
class SESSION_PT_advanced_settings(bpy.types.Panel):
|
||||||
bl_idname = "MULTIUSER_SETTINGS_REPLICATION_PT_panel"
|
bl_idname = "MULTIUSER_SETTINGS_REPLICATION_PT_panel"
|
||||||
bl_label = "Advanced"
|
bl_label = "General Settings"
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
bl_region_type = 'UI'
|
bl_region_type = 'UI'
|
||||||
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
bl_parent_id = 'MULTIUSER_SETTINGS_PT_panel'
|
||||||
@ -216,30 +267,19 @@ class SESSION_PT_advanced_settings(bpy.types.Panel):
|
|||||||
layout = self.layout
|
layout = self.layout
|
||||||
settings = get_preferences()
|
settings = get_preferences()
|
||||||
|
|
||||||
#ADVANCED HOST
|
#ADVANCED USER INFO
|
||||||
host_selection = layout.row().box()
|
uinfo_section = layout.row().box()
|
||||||
host_selection.prop(
|
uinfo_section.prop(
|
||||||
settings, "sidebar_advanced_hosting_expanded", text="Hosting",
|
settings,
|
||||||
icon=get_expanded_icon(settings.sidebar_advanced_hosting_expanded),
|
"sidebar_advanced_uinfo_expanded",
|
||||||
|
text="User Info",
|
||||||
|
icon=get_expanded_icon(settings.sidebar_advanced_uinfo_expanded),
|
||||||
emboss=False)
|
emboss=False)
|
||||||
if settings.sidebar_advanced_hosting_expanded:
|
if settings.sidebar_advanced_uinfo_expanded:
|
||||||
host_selection_row = host_selection.row()
|
uinfo_section_row = uinfo_section.row()
|
||||||
host_selection_row.prop(settings, "host_port", text="Port:")
|
uinfo_section_split = uinfo_section_row.split(factor=0.7, align=True)
|
||||||
host_selection_row = host_selection.row()
|
uinfo_section_split.prop(settings, "username", text="")
|
||||||
host_selection_row.label(text="Init the session from:")
|
uinfo_section_split.prop(settings, "client_color", text="")
|
||||||
host_selection_row.prop(settings, "init_method", text="")
|
|
||||||
host_selection_row = host_selection.row()
|
|
||||||
host_selection_col = host_selection_row.column()
|
|
||||||
host_selection_col.prop(settings, "host_use_server_password", text="Server password:")
|
|
||||||
host_selection_col = host_selection_row.column()
|
|
||||||
host_selection_col.enabled = True if settings.host_use_server_password else False
|
|
||||||
host_selection_col.prop(settings, "host_server_password", text="")
|
|
||||||
host_selection_row = host_selection.row()
|
|
||||||
host_selection_col = host_selection_row.column()
|
|
||||||
host_selection_col.prop(settings, "host_use_admin_password", text="Admin password:")
|
|
||||||
host_selection_col = host_selection_row.column()
|
|
||||||
host_selection_col.enabled = True if settings.host_use_admin_password else False
|
|
||||||
host_selection_col.prop(settings, "host_admin_password", text="")
|
|
||||||
|
|
||||||
#ADVANCED NET
|
#ADVANCED NET
|
||||||
net_section = layout.row().box()
|
net_section = layout.row().box()
|
||||||
@ -543,8 +583,7 @@ class SESSION_PT_repository(bpy.types.Panel):
|
|||||||
admin = usr['admin']
|
admin = usr['admin']
|
||||||
return hasattr(context.window_manager, 'session') and \
|
return hasattr(context.window_manager, 'session') and \
|
||||||
session and \
|
session and \
|
||||||
(session.state == STATE_ACTIVE or \
|
session.state == STATE_ACTIVE and \
|
||||||
session.state == STATE_LOBBY and admin) and \
|
|
||||||
not settings.sidebar_repository_shown
|
not settings.sidebar_repository_shown
|
||||||
|
|
||||||
def draw_header(self, context):
|
def draw_header(self, context):
|
||||||
@ -590,12 +629,6 @@ class SESSION_PT_repository(bpy.types.Panel):
|
|||||||
else:
|
else:
|
||||||
layout.row().label(text="Empty")
|
layout.row().label(text="Empty")
|
||||||
|
|
||||||
elif session.state == STATE_LOBBY and usr and usr['admin']:
|
|
||||||
row = layout.row()
|
|
||||||
row.operator("session.init", icon='TOOL_SETTINGS', text="Init")
|
|
||||||
else:
|
|
||||||
row = layout.row()
|
|
||||||
row.label(text="Waiting to start")
|
|
||||||
|
|
||||||
class VIEW3D_PT_overlay_session(bpy.types.Panel):
|
class VIEW3D_PT_overlay_session(bpy.types.Panel):
|
||||||
bl_space_type = 'VIEW_3D'
|
bl_space_type = 'VIEW_3D'
|
||||||
@ -614,6 +647,9 @@ class VIEW3D_PT_overlay_session(bpy.types.Panel):
|
|||||||
pref = get_preferences()
|
pref = get_preferences()
|
||||||
layout.active = settings.enable_presence
|
layout.active = settings.enable_presence
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(settings, "enable_presence",text="Presence Overlay")
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
row.prop(settings, "presence_show_selected",text="Selected Objects")
|
row.prop(settings, "presence_show_selected",text="Selected Objects")
|
||||||
|
|
||||||
@ -667,6 +703,7 @@ classes = (
|
|||||||
SESSION_UL_users,
|
SESSION_UL_users,
|
||||||
SESSION_UL_network,
|
SESSION_UL_network,
|
||||||
SESSION_PT_settings,
|
SESSION_PT_settings,
|
||||||
|
SESSION_PT_host_settings,
|
||||||
SESSION_PT_advanced_settings,
|
SESSION_PT_advanced_settings,
|
||||||
SESSION_PT_user,
|
SESSION_PT_user,
|
||||||
SESSION_PT_sync,
|
SESSION_PT_sync,
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# Download base image debian jessie
|
# Download base image debian jessie
|
||||||
FROM python:slim
|
FROM python:slim
|
||||||
|
|
||||||
ARG replication_version=0.1.13
|
ARG replication_version=0.9.1
|
||||||
ARG version=0.1.1
|
ARG version=0.1.1
|
||||||
|
|
||||||
# Infos
|
# Infos
|
||||||
@ -22,4 +22,4 @@ RUN pip install replication==$replication_version
|
|||||||
|
|
||||||
# Run the server with parameters
|
# Run the server with parameters
|
||||||
ENTRYPOINT ["/bin/sh", "-c"]
|
ENTRYPOINT ["/bin/sh", "-c"]
|
||||||
CMD ["python3 -m replication.server -pwd ${password} -p ${port} -t ${timeout} -l ${log_level} -lf ${log_file}"]
|
CMD ["replication.serve -apwd ${password} -spwd '' -p ${port} -t ${timeout} -l ${log_level} -lf ${log_file}"]
|
@ -7,7 +7,7 @@ import bpy
|
|||||||
from multi_user.bl_types.bl_lightprobe import BlLightprobe
|
from multi_user.bl_types.bl_lightprobe import BlLightprobe
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(bpy.app.version[1] < 83, reason="requires blender 2.83 or higher")
|
@pytest.mark.skipif(bpy.app.version < (2,83,0), reason="requires blender 2.83 or higher")
|
||||||
@pytest.mark.parametrize('lightprobe_type', ['PLANAR','GRID','CUBEMAP'])
|
@pytest.mark.parametrize('lightprobe_type', ['PLANAR','GRID','CUBEMAP'])
|
||||||
def test_lightprobes(clear_blend, lightprobe_type):
|
def test_lightprobes(clear_blend, lightprobe_type):
|
||||||
bpy.ops.object.lightprobe_add(type=lightprobe_type)
|
bpy.ops.object.lightprobe_add(type=lightprobe_type)
|
||||||
|
@ -1,20 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
from deepdiff import DeepDiff
|
|
||||||
|
|
||||||
import bpy
|
|
||||||
import random
|
|
||||||
|
|
||||||
|
|
||||||
def test_start_session():
|
|
||||||
result = bpy.ops.session.start()
|
|
||||||
|
|
||||||
|
|
||||||
assert 'FINISHED' in result
|
|
||||||
|
|
||||||
def test_stop_session():
|
|
||||||
|
|
||||||
result = bpy.ops.session.stop()
|
|
||||||
|
|
||||||
assert 'FINISHED' in result
|
|
Reference in New Issue
Block a user