Compare commits

...

48 Commits
dialog ... main

Author SHA1 Message Date
c596b760cf Fixing elevation seams
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Inventory API / deploy (push) Successful in 47s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 8s
2026-03-21 13:20:58 -05:00
f90de8a98b Adding elevation
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 1m0s
Deploy Promiscuity Inventory API / deploy (push) Successful in 47s
Deploy Promiscuity Locations API / deploy (push) Successful in 1m0s
k8s smoke test / test (push) Successful in 9s
2026-03-21 13:09:41 -05:00
c5e692c051 Fixing drop/pickup stacking
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 47s
Deploy Promiscuity Inventory API / deploy (push) Successful in 59s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 9s
2026-03-20 13:18:22 -05:00
525f9442c3 Adding Kenney UI and item management
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 47s
Deploy Promiscuity Inventory API / deploy (push) Successful in 59s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 10s
2026-03-20 13:11:47 -05:00
038981d7b1 Speeding up visible location call
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 57s
Deploy Promiscuity Inventory API / deploy (push) Successful in 59s
Deploy Promiscuity Locations API / deploy (push) Successful in 58s
k8s smoke test / test (push) Successful in 8s
2026-03-20 09:50:17 -05:00
8ce6a05710 Keeping logic within microservice boundaries
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 57s
Deploy Promiscuity Inventory API / deploy (push) Successful in 45s
Deploy Promiscuity Locations API / deploy (push) Successful in 58s
k8s smoke test / test (push) Successful in 8s
2026-03-20 09:11:44 -05:00
1320e0a0ac Nudging the location inventory display location
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Inventory API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 45s
k8s smoke test / test (push) Successful in 9s
2026-03-19 17:33:28 -05:00
b8ce13f1d2 Adding inventory stacking and overflow to gather mechanic
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Inventory API / deploy (push) Successful in 58s
Deploy Promiscuity Locations API / deploy (push) Successful in 58s
k8s smoke test / test (push) Successful in 9s
2026-03-19 17:24:10 -05:00
69ff204c5d Fixing model mismatch
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Inventory API / deploy (push) Successful in 47s
Deploy Promiscuity Locations API / deploy (push) Successful in 59s
k8s smoke test / test (push) Successful in 8s
2026-03-19 17:07:24 -05:00
0fc023b75c Fixing gather bug
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 45s
Deploy Promiscuity Inventory API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 58s
k8s smoke test / test (push) Successful in 8s
2026-03-19 17:01:47 -05:00
52a76625a3 Location error handling
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 45s
Deploy Promiscuity Inventory API / deploy (push) Successful in 45s
Deploy Promiscuity Locations API / deploy (push) Successful in 1m0s
k8s smoke test / test (push) Successful in 8s
2026-03-19 16:54:06 -05:00
82e4ae8a81 Merge branch 'main' of https://git.ranaze.com/null/promiscuity into main
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 58s
Deploy Promiscuity Inventory API / deploy (push) Successful in 45s
Deploy Promiscuity Locations API / deploy (push) Successful in 59s
k8s smoke test / test (push) Successful in 7s
2026-03-19 16:44:12 -05:00
1aefd5ba88 Adding interactable object spawning 2026-03-19 16:35:07 -05:00
0b15fb02d2 Merge pull request 'gather' (#9) from gather into main
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 1m0s
Deploy Promiscuity Inventory API / deploy (push) Successful in 45s
Deploy Promiscuity Locations API / deploy (push) Successful in 58s
k8s smoke test / test (push) Successful in 7s
Reviewed-on: #9
2026-03-19 13:49:11 -05:00
6d97e5324c Biome generation 2026-03-19 13:45:33 -05:00
fedb426cfc Merge branch 'main' into gather 2026-03-19 12:38:59 -05:00
50d44e8387 Allowing player to gather resources 2026-03-19 12:38:45 -05:00
932f7be66b Merge branches 'main' and 'main' of https://git.ranaze.com/null/promiscuity
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 45s
Deploy Promiscuity Inventory API / deploy (push) Successful in 45s
Deploy Promiscuity Locations API / deploy (push) Successful in 45s
k8s smoke test / test (push) Successful in 8s
# Conflicts:
#	game/scenes/Levels/level.tscn
2026-03-19 11:36:58 -05:00
b5c7c7c8b6 Grassy knoll 2026-03-19 11:35:38 -05:00
9ba725d207 Gathering infra
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 45s
Deploy Promiscuity Inventory API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 1m3s
k8s smoke test / test (push) Successful in 7s
2026-03-15 14:04:12 -05:00
a2a4d48de5 Inventory enhancements
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Inventory API / deploy (push) Successful in 58s
Deploy Promiscuity Locations API / deploy (push) Successful in 44s
k8s smoke test / test (push) Successful in 7s
2026-03-15 13:57:15 -05:00
5287ecd56f Adding workflow for inventory microservice
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 1m6s
Deploy Promiscuity Character API / deploy (push) Successful in 48s
Deploy Promiscuity Inventory API / deploy (push) Successful in 1m42s
Deploy Promiscuity Locations API / deploy (push) Successful in 47s
k8s smoke test / test (push) Successful in 7s
2026-03-15 13:15:49 -05:00
9a7d6544ef Adding inventory microservice
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 7s
2026-03-15 10:21:49 -05:00
9809869cbe Endless location generation
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 45s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 7s
2026-03-13 21:55:18 -05:00
e79f473ce4 Updating character location based on occupied tile
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 57s
Deploy Promiscuity Locations API / deploy (push) Successful in 44s
k8s smoke test / test (push) Successful in 7s
2026-03-13 21:34:59 -05:00
6ca0f306bb Fixing bad location index
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 57s
Deploy Promiscuity Locations API / deploy (push) Successful in 57s
k8s smoke test / test (push) Successful in 8s
2026-03-13 21:26:36 -05:00
9b646f501c Adding loggin to troubleshoot location generation issue
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 57s
Deploy Promiscuity Locations API / deploy (push) Successful in 45s
k8s smoke test / test (push) Successful in 7s
2026-03-13 13:59:04 -05:00
3b2cc9721b Fixing coord bug
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 59s
Deploy Promiscuity Locations API / deploy (push) Successful in 43s
k8s smoke test / test (push) Successful in 7s
2026-03-13 13:37:27 -05:00
c296ebc9cb Fixing coord bug
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 57s
Deploy Promiscuity Locations API / deploy (push) Successful in 44s
k8s smoke test / test (push) Successful in 7s
2026-03-13 13:14:30 -05:00
d615527dcc Fixing coord bug
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 58s
Deploy Promiscuity Locations API / deploy (push) Successful in 45s
k8s smoke test / test (push) Successful in 8s
2026-03-13 13:00:50 -05:00
097ef4a22c Adding endpoint to only get locations visible to character
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 59s
Deploy Promiscuity Locations API / deploy (push) Successful in 45s
k8s smoke test / test (push) Successful in 7s
2026-03-13 12:46:30 -05:00
84f8087647 Adding endpoint to only get locations visible to character
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 59s
Deploy Promiscuity Locations API / deploy (push) Successful in 44s
k8s smoke test / test (push) Successful in 7s
2026-03-13 10:49:57 -05:00
4ba06bf7e0 Fixing data driven level, that was blown away 2026-03-13 10:25:43 -05:00
e5dd8a7d04 Changing teleporters to be physical arches 2026-03-13 10:02:03 -05:00
393521ae8c Adding kenney prototype textures 2026-03-12 15:01:28 -05:00
a689b75651 Fixing zoom range bug
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 8s
2026-03-06 12:38:14 -06:00
0a8cf20de9 Adding scene teleporter
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 46s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 8s
2026-03-06 12:34:59 -06:00
2850c26657 Adding location based tiling 2026-03-06 12:04:02 -06:00
42e49b5cd8 Fix walk run animation loop snap
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Locations API / deploy (push) Successful in 45s
k8s smoke test / test (push) Successful in 7s
2026-03-03 11:53:07 -06:00
30580b5290 Experimenting with multiple packed scene instances.
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 7s
2026-03-02 13:03:43 -06:00
a77dc47756 Fixed the sky I broke.
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 45s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 7s
2026-03-02 12:57:54 -06:00
d39d719c7d Anim fix?
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 44s
Deploy Promiscuity Locations API / deploy (push) Successful in 45s
k8s smoke test / test (push) Successful in 7s
2026-02-25 19:48:43 -06:00
bf1ac4406c A raging quest 2026-02-25 19:46:10 -06:00
2670392e2d Added animation tree to player model
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 43s
Deploy Promiscuity Locations API / deploy (push) Successful in 44s
k8s smoke test / test (push) Successful in 7s
2026-02-20 13:48:02 -06:00
f4ff1308b8 Added hidden card game.
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 46s
Deploy Promiscuity Character API / deploy (push) Successful in 43s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 7s
2026-02-13 12:30:24 -06:00
6dc4d53ba0 Dialog bug fix/tweaks
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 47s
Deploy Promiscuity Character API / deploy (push) Successful in 43s
Deploy Promiscuity Locations API / deploy (push) Successful in 44s
k8s smoke test / test (push) Successful in 7s
2026-02-13 11:55:19 -06:00
ba6edd455a Merge pull request 'Adding simple dialog system' (#8) from dialog into main
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 49s
Deploy Promiscuity Character API / deploy (push) Successful in 1m33s
Deploy Promiscuity Locations API / deploy (push) Successful in 1m15s
k8s smoke test / test (push) Successful in 21s
Reviewed-on: #8
2026-02-13 11:44:13 -06:00
6f809c3684 Update README.md
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 45s
Deploy Promiscuity Character API / deploy (push) Successful in 42s
Deploy Promiscuity Locations API / deploy (push) Successful in 46s
k8s smoke test / test (push) Successful in 7s
2026-02-13 11:12:54 -06:00
3028 changed files with 89397 additions and 449 deletions

View File

@ -0,0 +1,112 @@
name: Deploy Promiscuity Inventory API
on:
push:
branches:
- main
workflow_dispatch: {}
jobs:
deploy:
runs-on: self-hosted
env:
IMAGE_NAME: promiscuity-inventory:latest
IMAGE_TAR: /tmp/promiscuity-inventory.tar
# All nodes that might run the pod (control-plane + workers)
NODES: "192.168.86.72 192.168.86.73 192.168.86.74"
steps:
- name: Checkout repo
uses: actions/checkout@v4
# -----------------------------
# Build Docker image
# -----------------------------
- name: Build Docker image
run: |
cd microservices/InventoryApi
docker build -t "${IMAGE_NAME}" .
# -----------------------------
# Save image as TAR on runner
# -----------------------------
- name: Save Docker image to TAR
run: |
docker save "${IMAGE_NAME}" -o "${IMAGE_TAR}"
# -----------------------------
# Copy TAR to each Kubernetes node
# -----------------------------
- name: Copy TAR to nodes
run: |
for node in ${NODES}; do
echo "Copying image tar to $node ..."
scp -o StrictHostKeyChecking=no "${IMAGE_TAR}" hz@"$node":/tmp/promiscuity-inventory.tar
done
# -----------------------------
# Import image into containerd on each node
# -----------------------------
- name: Import image on nodes
run: |
for node in ${NODES}; do
echo "Importing image on $node ..."
ssh -o StrictHostKeyChecking=no hz@"$node" "sudo ctr -n k8s.io images import /tmp/promiscuity-inventory.tar"
done
# -----------------------------
# CLEANUP: delete TAR from nodes
# -----------------------------
- name: Clean TAR from nodes
run: |
for node in ${NODES}; do
echo "Removing image tar on $node ..."
ssh -o StrictHostKeyChecking=no hz@"$node" "rm -f /tmp/promiscuity-inventory.tar"
done
# -----------------------------
# CLEANUP: delete TAR from runner
# -----------------------------
- name: Clean TAR on runner
run: |
rm -f "${IMAGE_TAR}"
# -----------------------------
# Write kubeconfig from secret
# -----------------------------
- name: Write kubeconfig from secret
env:
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
run: |
mkdir -p /tmp/kube
printf '%s\n' "$KUBECONFIG_CONTENT" > /tmp/kube/config
# -----------------------------
# Ensure namespace exists
# -----------------------------
- name: Create namespace if missing
env:
KUBECONFIG: /tmp/kube/config
run: |
kubectl create namespace promiscuity-inventory --dry-run=client -o yaml | kubectl apply -f -
# -----------------------------
# Apply Kubernetes manifests
# -----------------------------
- name: Apply Inventory deployment & service
env:
KUBECONFIG: /tmp/kube/config
run: |
kubectl apply -f microservices/InventoryApi/k8s/deployment.yaml -n promiscuity-inventory
kubectl apply -f microservices/InventoryApi/k8s/service.yaml -n promiscuity-inventory
# -----------------------------
# Rollout restart & wait
# -----------------------------
- name: Restart Inventory deployment
env:
KUBECONFIG: /tmp/kube/config
run: |
kubectl rollout restart deployment/promiscuity-inventory -n promiscuity-inventory
kubectl rollout status deployment/promiscuity-inventory -n promiscuity-inventory

View File

@ -15,6 +15,7 @@
## Controls
- Move: WASD
- Jump: Space
- Sprint: Shift
- Interact (enter/exit car): E
- Flashlight: F
- Phone: Tab

View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 IcterusGames
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,15 @@
[gd_resource type="ArrayMesh" format=4 uid="uid://sptcpdx6wtm8"]
[resource]
_surfaces = [{
"aabb": AABB(-0.5, 0, -0.5, 1, 1, 1),
"attribute_data": PackedByteArray("/wAA/wAAAAAAAAAAAAAA/wAAgD8AAIA/AAAA/wAAAAAAAIA//wAA/wAAgD8AAAAA/wAA/wAAAAAAAAAAAAAA/wAAgD8AAIA/AAAA/wAAAAAAAIA//wAA/wAAgD8AAAAA"),
"format": 34359742495,
"index_count": 12,
"index_data": PackedByteArray("AAABAAIAAwABAAAABAAFAAYABwAFAAQA"),
"primitive": 3,
"uv_scale": Vector4(0, 0, 0, 0),
"vertex_count": 8,
"vertex_data": PackedByteArray("AAAAvwAAgD8AAAAAAAAAPwAAAAAAAAAAAAAAvwAAAAAAAAAAAAAAPwAAgD8AAAAAAAAAAAAAgD8AAAC/AAAAAAAAAAAAAAA/AAAAAAAAAAAAAAC/AAAAAAAAgD8AAAA//3//f////7//f/9/////v/9//3////+//3//f////78AAP9//3//vwAA/3//f/+/AAD/f/9//78AAP9//3//vw==")
}]
metadata/GodotVersion = "4.5-stable (official)"

View File

@ -0,0 +1,100 @@
# default_mesh_builder.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## Builds and saves the default grass mesh when called.
func rebuild_and_save_default_mesh() -> void:
var array_mesh := ArrayMesh.new()
var vertices := PackedVector3Array()
var normals := PackedVector3Array()
var tangents := PackedFloat32Array()
var colors := PackedColorArray()
var uvs := PackedVector2Array()
var index := PackedInt32Array()
vertices.push_back(Vector3(-0.5, 1, 0))
vertices.push_back(Vector3(0.5, 0, 0))
vertices.push_back(Vector3(-0.5, 0, 0))
vertices.push_back(Vector3(0.5, 1, 0))
vertices.push_back(Vector3(0, 1, -0.5))
vertices.push_back(Vector3(0, 0, 0.5))
vertices.push_back(Vector3(0, 0, -0.5))
vertices.push_back(Vector3(0, 1, 0.5))
normals.push_back(Vector3(0, 0, 1))
normals.push_back(Vector3(0, 0, 1))
normals.push_back(Vector3(0, 0, 1))
normals.push_back(Vector3(0, 0, 1))
normals.push_back(Vector3(-1, 0, 0))
normals.push_back(Vector3(-1, 0, 0))
normals.push_back(Vector3(-1, 0, 0))
normals.push_back(Vector3(-1, 0, 0))
for i in range(4):
tangents.push_back(1)
tangents.push_back(0)
tangents.push_back(0)
tangents.push_back(1)
for i in range(4):
tangents.push_back(0)
tangents.push_back(0)
tangents.push_back(1)
tangents.push_back(1)
uvs.push_back(Vector2(0, 0))
uvs.push_back(Vector2(1, 1))
uvs.push_back(Vector2(0, 1))
uvs.push_back(Vector2(1, 0))
uvs.push_back(Vector2(0, 0))
uvs.push_back(Vector2(1, 1))
uvs.push_back(Vector2(0, 1))
uvs.push_back(Vector2(1, 0))
colors.push_back(Color(1, 0, 0))
colors.push_back(Color(0, 0, 0))
colors.push_back(Color(0, 0, 0))
colors.push_back(Color(1, 0, 0))
colors.push_back(Color(1, 0, 0))
colors.push_back(Color(0, 0, 0))
colors.push_back(Color(0, 0, 0))
colors.push_back(Color(1, 0, 0))
index.push_back(0)
index.push_back(1)
index.push_back(2)
index.push_back(3)
index.push_back(1)
index.push_back(0)
index.push_back(4)
index.push_back(5)
index.push_back(6)
index.push_back(7)
index.push_back(5)
index.push_back(4)
var arrays := []
arrays.resize(Mesh.ARRAY_MAX)
arrays[Mesh.ARRAY_VERTEX] = vertices
arrays[ArrayMesh.ARRAY_NORMAL] = normals
arrays[ArrayMesh.ARRAY_TANGENT] = tangents
arrays[ArrayMesh.ARRAY_TEX_UV] = uvs
arrays[ArrayMesh.ARRAY_COLOR] = colors
arrays[ArrayMesh.ARRAY_INDEX] = index
array_mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
array_mesh.set_meta(&"GodotVersion", Engine.get_version_info()["string"])
ResourceSaver.save(array_mesh, "res://addons/simplegrasstextured/default_mesh.tres")

View File

@ -0,0 +1 @@
uid://deh0tfs84csxo

View File

@ -0,0 +1,973 @@
# grass.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends MultiMeshInstance3D
## Define your custom mesh per grass, you can open a .obj, .mesh, etc, or can
## copy paste your own mesh from any mesh component. Set as null for default
## SimpleGrassTextured mesh.
@export var mesh : Mesh = null : set = _on_set_mesh
## Color albedo for mesh material
@export_color_no_alpha var albedo := Color.WHITE : set = _on_set_albedo
## Texture albedo for mesh, you can apply normal, metallic and roughness
## textures on the "Material parameters" section
@export var texture_albedo : Texture = load("res://addons/simplegrasstextured/textures/grassbushcc008.png") : set = _on_set_texture_albedo
@export_group("Material parameters")
## Lets you setup a multi texture image by frames
@export var texture_frames : Vector2i = Vector2i(1, 1) : set = _on_set_texture_frames;
## Defines the texture image alpha threshold
@export_range(0.0, 1.0) var alpha_scissor_threshold := 0.5 : set = _on_set_alpha_scissor_threshold
## Ilumination mode[br]
## [b]Lambert[/b][br]Recomended for complex meshes such as flowers[br][br]
## [b]Normal grass[/b][br]The lighting will be calculated by the inclination of
## the grass, recommended for very simple meshes[br][br]
## [b]Unshaded[/b][br]No lighting will affect the grass
@export_enum("Lambert", "Normal grass", "Unshaded") var light_mode := 1 : set = _on_set_light_mode
@export_enum("Nearest", "Linear", "Nearest mipmap", "Linear mipmap") var texture_filter := 3 : set = _on_set_texture_filter
@export_subgroup("Normal")
@export var texture_normal : Texture = null : set = _on_set_texture_normal
@export_range(-16.0, 16.0) var normal_scale := 1.0 : set = _on_set_normal_scale
@export_subgroup("Metallic")
@export var texture_metallic : Texture = null : set = _on_set_texture_metallic
@export_enum("Red","Green","Blue","Alpha","Gray") var metallic_texture_channel : int = 0 : set = _on_set_metallic_texture_channel
@export_range(0.0, 1.0) var metallic := 0.0 : set = _on_set_metallic
@export_range(0.0, 1.0) var specular := 0.5 : set = _on_set_specular
@export_subgroup("Roughness")
@export var texture_roughness : Texture = null : set = _on_set_texture_roughness
@export_enum("Red","Green","Blue","Alpha","Gray") var roughness_texture_channel : int = 0 : set = _on_set_roughness_texture_channel
@export_range(0.0, 1.0) var roughness := 1.0 : set = _on_set_roughness
@export_group("")
## Scale height factor of all the grass
@export var scale_h := 1.0 : set = _on_set_scale_h
## Scale width factor of all the grass
@export var scale_w := 1.0 : set = _on_set_scale_w
## Scale variable factor, scale of random grasses will be affected by this
## factor
@export var scale_var := -0.25 : set = _on_set_scale_var
## Defines the strength of this grass, with large values the grass will not be
## moved by the wind, for example a bamboo can be 0.9 (it will almost not be
## affected by the wind), and a tall grass can be 0.2 (very affected by the
## wind)
@export_range(0.0, 1.0) var grass_strength := 0.55 : set = _on_set_grass_strength
## If true, this grass will be in "interactive mode", that means if an object
## is near the grass, the grass will react and move.[br][br]
## [b]To setup the "interactive mode":[/b][br]
## 1. You must enable by code on the begin of your scene by call
## [code]SimpleGrass.set_interactive(true)[/code][br]
## 2. Setup your objects to be visible on the Visual Layer 17[br]
## 3. Update the SimpleGrassTexture camera position by calling
## [code]SimpleGrass.set_player_position()[/code] regulary (on your _process
## function by example)[br][br]
## [b]You can see how to enable "interactive mode" on:[/b][br]
## [url]https://github.com/IcterusGames/SimpleGrassTextured?tab=readme-ov-file#how-to-enable-interactive-mode[/url]
@export var interactive : bool = true : set = _on_set_interactive
@export_group("Advanced")
## Allows you to define how much the grass will react to objects on axis X and Z
@export var interactive_level_xz : float = 3.0 : set = _on_set_interactive_level_xz
## Allows you to define how much the grass will react to objects on axis Y
@export var interactive_level_y : float = 0.3 : set = _on_set_interactive_level_y
## Locks the scale node of SimpleGrassTextured to 1
@export var disable_node_scale := true : set = _on_set_disable_node_scale
## Disable the ability to rotate the SimpleGrassTextured node
@export var disable_node_rotation := true : set = _on_set_disable_node_rotation
@export_group("Optimization")
@export var optimization_by_distance := false : set = _on_set_optimization_by_distance
@export var optimization_level := 7.0 : set = _on_set_optimization_level
@export var optimization_dist_min := 10.0 : set = _on_set_optimization_dist_min
@export var optimization_dist_max := 50.0 : set = _on_set_optimization_dist_max
@export_group("Height Map Data")
## This is the baked height map of this grass, this will speed up the load of
## the scene. To setup this variable use the menu
## SimpleGrassTextured->"Bake height map" on the Editor 3D
@export var baked_height_map : Image = null
@export_group("Draw Collision Mask")
## This is the collision mask for drawing, this allows you to define what your
## terrain collision mask is, that way it will be easier to draw your grass.
@export_flags_3d_physics var collision_mask :int = pow(2, 32) - 1
var sgt_radius := 2.0
var sgt_density := 25
var sgt_scale := 1.0
var sgt_rotation := 0.0
var sgt_rotation_rand := 1.0
var sgt_dist_min := 0.25
var sgt_follow_normal := false
var sgt_slope := Vector2(0, 45)
var sgt_tool_shape := {}
var temp_dist_min := 0.0
# Deprecated vars:
var player_pos := Vector3(1000000, 1000000, 1000000) : set = _on_set_player_pos
var player_radius := 0.5 : set = _on_set_player_radius
var wind_dir := Vector3.RIGHT : set = _on_set_wind_dir
var wind_strength := 0.15 : set = _on_set_wind_strength
var wind_turbulence := 1.0 : set = _on_set_wind_turbulence
var wind_pattern : Texture = null : set = _on_set_wind_pattern
var _default_mesh : Mesh = load("res://addons/simplegrasstextured/default_mesh.tres").duplicate()
var _buffer_add : Array[Transform3D] = []
var _material := load("res://addons/simplegrasstextured/materials/grass.material").duplicate() as ShaderMaterial
var _force_update_multimesh := false
var _properties = []
var _node_height_map = null
var _singleton = null
var _wrng_deprec_playerpos = true
var _wrng_deprec_playerrad = true
var _wrng_deprec_windir = true
var _wrng_deprec_windstrng = true
var _wrng_deprec_windturb = true
var _wrng_deprec_windpatt = true
func _init():
if Engine.is_editor_hint():
if collision_mask == pow(2, 32) - 1:
collision_mask = ProjectSettings.get_setting("SimpleGrassTextured/General/default_terrain_physics_layer", pow(2, 32) -1)
for var_i in get_property_list():
if not var_i.name.begins_with("sgt_"):
continue
_properties.append({
"name": var_i.name,
"type": var_i.type,
"usage": PROPERTY_USAGE_NO_EDITOR | PROPERTY_USAGE_SCRIPT_VARIABLE,
})
func _ready():
if Engine.is_editor_hint():
set_process(true)
else:
set_process(false)
_singleton = get_node("/root/SimpleGrass")
if not has_meta(&"SimpleGrassTextured"):
set_meta(&"SimpleGrassTextured", "2.0.5")
else:
if get_meta(&"SimpleGrassTextured") == "1.0.2":
# New default mesh update tangents
set_meta(&"SimpleGrassTextured", "2.0.3")
_force_update_multimesh = true
if multimesh != null:
if mesh != null:
multimesh.mesh = mesh
else:
multimesh.mesh = _default_mesh
if get_meta(&"SimpleGrassTextured") == "2.0.3":
set_meta(&"SimpleGrassTextured", "2.0.5")
disable_node_scale = false
disable_node_rotation = false
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.transform_format = MultiMesh.TRANSFORM_3D
if multimesh.mesh == null:
if mesh != null:
multimesh.mesh = mesh
else:
multimesh.mesh = _default_mesh
_update_material_shader()
for isur in range(multimesh.mesh.get_surface_count()):
if multimesh.mesh.surface_get_material(isur) != null:
_material = multimesh.mesh.surface_get_material(isur)
if _material.get_reference_count() > 2:
_material = _material.duplicate()
break
for isur in range(multimesh.mesh.get_surface_count()):
if multimesh.mesh.surface_get_material(isur) == null:
multimesh.mesh.surface_set_material(isur, _material)
set_disable_scale(disable_node_scale)
if disable_node_rotation:
set_notify_transform(true)
update_all_material()
func _notification(what: int) -> void:
if what == NOTIFICATION_TRANSFORM_CHANGED:
if disable_node_rotation and global_rotation != Vector3.ZERO:
var prev_scale := scale
global_rotation = Vector3.ZERO
scale = prev_scale
func update_all_material():
_on_set_albedo(albedo)
_on_set_texture_albedo(texture_albedo)
_on_set_alpha_scissor_threshold(alpha_scissor_threshold)
_on_set_light_mode(light_mode)
_on_set_texture_normal(texture_normal)
_on_set_normal_scale(normal_scale)
_on_set_texture_metallic(texture_metallic)
_on_set_metallic_texture_channel(metallic_texture_channel)
_on_set_metallic(metallic)
_on_set_specular(specular)
_on_set_texture_roughness(texture_roughness)
_on_set_roughness_texture_channel(roughness_texture_channel)
_on_set_roughness(roughness)
_on_set_scale_h(scale_h)
_on_set_scale_w(scale_w)
_on_set_scale_var(scale_var)
_on_set_grass_strength(grass_strength)
_on_set_interactive(interactive)
_on_set_interactive_level_xz(interactive_level_xz)
_on_set_interactive_level_y(interactive_level_y)
_on_set_optimization_by_distance(optimization_by_distance)
_on_set_optimization_level(optimization_level)
_on_set_optimization_dist_min(optimization_dist_min)
_on_set_optimization_dist_max(optimization_dist_max)
func _enter_tree():
if interactive:
_update_height_map.call_deferred()
func _exit_tree():
if _node_height_map:
_node_height_map.queue_free()
_node_height_map = null
func _process(_delta : float):
if _buffer_add.size() != 0 or _force_update_multimesh:
_force_update_multimesh = false
_update_multimesh()
func _get_property_list() -> Array:
if _properties == null:
return []
return _properties
func eval_grass_transform(pos : Vector3, normal : Vector3, scale : Vector3, rotated : float) -> Transform3D:
var trans := Transform3D()
if abs(normal.z) == 1:
trans.basis.x = Vector3(1,0,0)
trans.basis.y = Vector3(0,0,normal.z)
trans.basis.z = Vector3(0,normal.z,0)
trans.basis = trans.basis.orthonormalized()
else:
trans.basis.y = normal
trans.basis.x = normal.cross(trans.basis.z)
trans.basis.z = trans.basis.x.cross(normal)
trans.basis = trans.basis.orthonormalized()
trans = trans.rotated_local(Vector3.UP, rotated)
trans = trans.scaled(scale)
trans = trans.translated(pos)
return trans
func add_grass(pos : Vector3, normal : Vector3, scale : Vector3, rotated : float):
var trans := eval_grass_transform(pos, normal, scale, rotated)
if sgt_dist_min > 0:
for trans_prev in _buffer_add:
if trans.origin.distance_to(trans_prev.origin) <= sgt_dist_min:
return
_buffer_add.append(trans)
func add_grass_batch(transforms : Array):
var distmin = temp_dist_min
if temp_dist_min == 0:
distmin = sgt_dist_min
if _buffer_add.size() > 0 and distmin > 0:
for trans_prev in _buffer_add:
for trans in transforms:
if trans.origin.distance_to(trans_prev.origin) <= distmin:
transforms.erase(trans)
break
_buffer_add.append_array(transforms)
func erase(pos: Vector3, radius: float) -> void:
if not multimesh.get_aabb().intersects(AABB(pos - Vector3(radius, radius, radius), Vector3(radius, radius, radius) * 2)):
return
_apply_erase_tool(func(array: Array[Transform3D]) -> int:
var num_to_erase := 0
for i in range(multimesh.instance_count):
var trans := multimesh.get_instance_transform(i)
if trans.origin.distance_to(pos) > radius:
array.append(trans)
else:
num_to_erase += 1
return num_to_erase
)
func erase_cylinder(pos: Vector3, rx: float, height: float, rz: float, shape_transform: Transform3D) -> void:
var aabb := AABB(Vector3(-rx, -height / 2, -rz), Vector3(rx, height / 2, rz) * 2)
aabb = shape_transform * aabb
if not (global_transform * multimesh.get_aabb()).intersects(aabb):
return
_apply_erase_tool(func(array: Array[Transform3D]) -> int:
var num_to_erase := 0
for i in range(multimesh.instance_count):
var trans := multimesh.get_instance_transform(i)
var point := global_transform * trans.origin * shape_transform
var r = (point.x * point.x) / (rx * rx) + (point.z * point.z) / (rz * rz)
if point.y < -height or point.y > height or r >= 1:
array.append(trans)
else:
num_to_erase += 1
return num_to_erase
)
func erase_box(pos: Vector3, size: Vector3, shape_transform: Transform3D) -> void:
var aabb := AABB(-size / 2, size)
if not (global_transform * multimesh.get_aabb()).intersects(shape_transform * aabb):
return
_apply_erase_tool(func(array: Array[Transform3D]) -> int:
var num_to_erase := 0
for i in range(multimesh.instance_count):
var trans := multimesh.get_instance_transform(i)
if not aabb.has_point(global_transform * trans.origin * shape_transform):
array.append(trans)
else:
num_to_erase += 1
return num_to_erase
)
func snap_to_terrain() -> void:
await get_tree().physics_frame
for i in multimesh.instance_count:
var trans := multimesh.get_instance_transform(i)
var origin_ws := to_global(trans.origin)
var rc = _raycast(self, origin_ws + Vector3.UP * 100, Vector3.DOWN, 1000, collision_mask)
if not rc:
continue
trans.origin = to_local(rc["position"])
var normal : Vector3 = rc["normal"] if sgt_follow_normal else Vector3.UP
if abs(normal.z) == 1:
trans.basis.x = Vector3(1, 0, 0)
trans.basis.y = Vector3(0, 0, normal.z)
trans.basis.z = Vector3(0, normal.z, 0)
trans.basis = trans.basis.orthonormalized()
else:
trans.basis.y = normal
trans.basis.x = normal.cross(trans.basis.z)
trans.basis.z = trans.basis.x.cross(normal)
multimesh.set_instance_transform(i, trans)
recalculate_custom_aabb()
static func _raycast(from: Node3D, pos: Vector3, dir: Vector3, dist: float, mask: int) -> Dictionary:
var space_state = from.get_world_3d().direct_space_state
var query = PhysicsRayQueryParameters3D.create(pos, pos + dir * dist, mask, [from])
return space_state.intersect_ray(query)
func _apply_erase_tool(func_tool: Callable):
var multi_new := MultiMesh.new()
var array : Array[Transform3D] = []
multi_new.transform_format = MultiMesh.TRANSFORM_3D
if mesh != null:
multi_new.mesh = mesh
else:
multi_new.mesh = _default_mesh
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
if func_tool.call(array) == 0:
return
multi_new.instance_count = array.size()
for i in range(array.size()):
multi_new.set_instance_transform(i, array[i])
if Engine.is_editor_hint() and multimesh.resource_path.length():
var path := multimesh.resource_path
multimesh = multi_new
multimesh.take_over_path(path)
else:
multimesh = multi_new
if _material != null:
for isur in range(multimesh.mesh.get_surface_count()):
if multimesh.mesh.surface_get_material(isur) == null:
multimesh.mesh.surface_set_material(isur, _material)
if Engine.is_editor_hint():
baked_height_map = null
custom_aabb.position = Vector3.ZERO
custom_aabb.end = Vector3.ZERO
func auto_center_position():
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
var aabb : AABB = multimesh.get_aabb()
var center : Vector3 = global_position + aabb.position + (aabb.size / 2)
var align : Vector3 = global_position - center
if center == global_position:
return
global_position = center
var multi_new := MultiMesh.new()
multi_new.transform_format = MultiMesh.TRANSFORM_3D
if mesh != null:
multi_new.mesh = mesh
else:
multi_new.mesh = _default_mesh
multi_new.instance_count = multimesh.instance_count
for i in range(multimesh.instance_count):
var trans := multimesh.get_instance_transform(i)
trans.origin += align
multi_new.set_instance_transform(i, trans)
if Engine.is_editor_hint() and multimesh.resource_path.length():
var path := multimesh.resource_path
multimesh = multi_new
multimesh.take_over_path(path)
else:
multimesh = multi_new
if _material != null:
for isur in range(multimesh.mesh.get_surface_count()):
if multimesh.mesh.surface_get_material(isur) == null:
multimesh.mesh.surface_set_material(isur, _material)
if Engine.is_editor_hint():
if baked_height_map != null:
baked_height_map = null
bake_height_map()
custom_aabb.position = Vector3.ZERO
custom_aabb.end = Vector3.ZERO
else:
baked_height_map = null
func recalculate_custom_aabb():
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
var start := Vector3.ONE * 0x7FFFFFFF
var end := start * -1
var mesh_end := multimesh.mesh.get_aabb().end * Vector3(scale_w, scale_h, scale_w)
for i in range(multimesh.instance_count):
var trans := multimesh.get_instance_transform(i)
var point : Vector3 = trans * mesh_end
if point.x < start.x: start.x = point.x
if point.y < start.y: start.y = point.y
if point.z < start.z: start.z = point.z
point = trans * mesh_end
if point.x > end.x: end.x = point.x
if point.y > end.y: end.y = point.y
if point.z > end.z: end.z = point.z
custom_aabb.position = start
custom_aabb.end = end
func _update_multimesh():
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
var multi_new := MultiMesh.new()
var count_prev := multimesh.instance_count
multi_new.transform_format = MultiMesh.TRANSFORM_3D
if mesh != null:
multi_new.mesh = mesh
else:
multi_new.mesh = _default_mesh
if count_prev > 0 and _buffer_add.size() > 0 and (sgt_dist_min > 0 or temp_dist_min > 0):
var pos_min := Vector3(10000000, 10000000, 10000000)
var pos_max := pos_min * -1
for trans in _buffer_add:
if pos_min.x > trans.origin.x: pos_min.x = trans.origin.x
if pos_min.y > trans.origin.y: pos_min.y = trans.origin.y
if pos_min.z > trans.origin.z: pos_min.z = trans.origin.z
if pos_max.x < trans.origin.x: pos_max.x = trans.origin.x
if pos_max.y < trans.origin.y: pos_max.y = trans.origin.y
if pos_max.z < trans.origin.z: pos_max.z = trans.origin.z
pos_min -= Vector3.ONE
pos_max += Vector3.ONE
var dist_min := temp_dist_min
if dist_min == 0:
dist_min = sgt_dist_min
for i in range(multimesh.instance_count):
var trans := multimesh.get_instance_transform(i)
if trans.origin.x < pos_min.x or trans.origin.x > pos_max.x: continue
if trans.origin.y < pos_min.y or trans.origin.y > pos_max.y: continue
if trans.origin.z < pos_min.z or trans.origin.z > pos_max.z: continue
for trans_add in _buffer_add:
if trans_add.origin.distance_to(trans.origin) > dist_min:
continue
_buffer_add.erase(trans_add)
break
if _buffer_add.size() == 0:
return
multi_new.instance_count = count_prev + _buffer_add.size()
for i in range(multimesh.instance_count):
multi_new.set_instance_transform(i, multimesh.get_instance_transform(i))
for i in range(_buffer_add.size()):
multi_new.set_instance_transform(i + count_prev, _buffer_add[i])
if Engine.is_editor_hint() and multimesh.resource_path.length():
var path := multimesh.resource_path
multimesh = multi_new
multimesh.take_over_path(path)
else:
multimesh = multi_new
if _material != null:
for isur in range(multimesh.mesh.get_surface_count()):
if multimesh.mesh.surface_get_material(isur) == null:
multimesh.mesh.surface_set_material(isur, _material)
_buffer_add.clear()
temp_dist_min = 0
if Engine.is_editor_hint():
baked_height_map = null
custom_aabb.position = Vector3.ZERO
custom_aabb.end = Vector3.ZERO
func _create_height_map_image(local : bool) -> Image:
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
var aabb : AABB = multimesh.get_aabb()
var img_size := Vector2i(
clamp(snappedi(aabb.size.x * 4, 32), 32, 128),
clamp(snappedi(aabb.size.z * 4, 32), 32, 128)
)
var img := Image.create(img_size.x, img_size.y, false, Image.FORMAT_RGBA8)
var relx := float(img_size.x) / aabb.size.x
var relz := float(img_size.y) / aabb.size.z
img.fill(Color(1.0, 1.0, 1.0, 0.0))
for i in range(multimesh.instance_count):
var trans : Transform3D = multimesh.get_instance_transform(i)
trans.origin -= aabb.position
var x := clampi(int(trans.origin.x * relx), 0, img_size.x)
var y := clampi(int(trans.origin.z * relz), 0, img_size.y)
var posy := trans.origin.y + aabb.position.y + 16200.0
if local:
posy += global_position.y
var r := (floorf(posy / 180.0) + 75.0) / 255.0
var g := (floorf(posy - ((roundf(r * 255.0) - 75.0) * 180.0)) + 75.0) / 255.0
var b := fmod(absf(posy), 1.0)
var color := Color(r, g, b, 1.0)
img.set_pixel(x, y, color)
for n in range(1, 3):
if x - n >= 0 and img.get_pixel(x - n, y).a == 0:
img.set_pixel(x - n, y, color)
if y - n >= 0 and img.get_pixel(x - n, y - n).a == 0:
img.set_pixel(x - n, y - n, color)
if y + n < img_size.y and img.get_pixel(x - n, y + n).a == 0:
img.set_pixel(x - n, y + n, color)
if x + n < img_size.x and img.get_pixel(x + n, y).a == 0:
img.set_pixel(x + n, y, color)
if y - n >= 0 and img.get_pixel(x + n, y - n).a == 0:
img.set_pixel(x + n, y - n, color)
if y + n < img_size.y and img.get_pixel(x + n, y + n).a == 0:
img.set_pixel(x + n, y + n, color)
if y - n >= 0 and img.get_pixel(x, y - n).a == 0:
img.set_pixel(x, y - n, color)
if y + n < img_size.y and img.get_pixel(x, y + n).a == 0:
img.set_pixel(x, y + n, color)
return img
func _local_height_map_to_global(img : Image) -> Image:
var result : Image = Image.create(img.get_width(), img.get_height(), false, Image.FORMAT_RGBA8)
result.fill(Color(0, 0, 0, 0))
for y in img.get_height():
for x in img.get_width():
var color : Color = img.get_pixel(x, y)
if color.a == 0:
continue
var posy : float = (((roundf(color.r * 255.0) - 75.0) * 180.0) + (roundf(color.g * 255.0) - 75.0) + color.b)
posy += global_position.y
color.r = (floorf(posy / 180.0) + 75.0) / 255.0
color.g = (floorf(posy - ((roundf(color.r * 255.0) - 75.0) * 180.0)) + 75.0) / 255.0
color.b = fmod(absf(posy), 1.0)
result.set_pixel(x, y, color)
return result
func bake_height_map():
if not Engine.is_editor_hint():
return null
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
await get_tree().process_frame
var _dummy = multimesh.buffer.size()
await get_tree().process_frame
var img : Image = _create_height_map_image(false)
baked_height_map = img
func clear_all():
if multimesh == null:
multimesh = MultiMesh.new()
if Engine.is_editor_hint() and multimesh.resource_path.length():
var path := multimesh.resource_path
multimesh = MultiMesh.new()
multimesh.take_over_path(path)
else:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
multimesh.transform_format = MultiMesh.TRANSFORM_3D
if Engine.is_editor_hint():
if baked_height_map != null:
baked_height_map = null
bake_height_map()
custom_aabb.position = Vector3.ZERO
custom_aabb.end = Vector3.ZERO
func _update_height_map():
if Engine.is_editor_hint():
return
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
var img : Image = null
if baked_height_map == null:
await get_tree().process_frame
var _dummy = multimesh.buffer.size()
await get_tree().process_frame
img = _create_height_map_image(true)
else:
img = _local_height_map_to_global(baked_height_map)
var aabb : AABB = multimesh.get_aabb()
var texture := ImageTexture.create_from_image(img)
if _node_height_map != null:
_node_height_map.queue_free()
_node_height_map = MeshInstance3D.new()
_node_height_map.mesh = PlaneMesh.new()
_node_height_map.mesh.size = Vector2(aabb.size.x, aabb.size.z)
var mat = load("res://addons/simplegrasstextured/materials/position.material").duplicate(true)
mat.set_shader_parameter("texture_albedo", texture)
_node_height_map.material_override = mat
_singleton._height_view.add_child(_node_height_map)
var align := Vector3(
(aabb.position.x + (aabb.size.x / 2.0)),
0,
(aabb.position.z + (aabb.size.z / 2.0))
)
_node_height_map.global_position = global_position + align
_node_height_map.visible = visible
func _update_material_shader() -> bool:
var shader_name := "grass"
if light_mode == 2:
shader_name += "_unshaded"
# "Nearest" = 0, "Linear" = 1, "Nearest mipmap" = 2, "Linear mipmap" = 3
if texture_filter == 0:
shader_name += "_nearest"
elif texture_filter == 1:
shader_name += "_linear"
elif texture_filter == 2:
shader_name += "_nearest_mipmap"
elif texture_filter == 3:
shader_name += "" # Linear mipmap is the default filter
if _material.get_shader().resource_path != "res://addons/simplegrasstextured/shaders/" + shader_name + ".gdshader":
_material.shader = load("res://addons/simplegrasstextured/shaders/" + shader_name + ".gdshader")
if _material.get_shader() == null:
_material.shader = load("res://addons/simplegrasstextured/shaders/grass.gdshader")
_material.shader.take_over_path("res://addons/simplegrasstextured/shaders/" + shader_name + ".gdshader")
return true
return false
func _on_set_mesh(value : Mesh):
mesh = value
if _material != null:
if mesh != null:
_material.set_shader_parameter("grass_size_y", mesh.get_aabb().size.y)
else:
_material.set_shader_parameter("grass_size_y", 1.0)
if Engine.is_editor_hint() and is_inside_tree():
_update_multimesh()
func _on_set_albedo(value : Color):
albedo = value;
if _material != null:
_material.set_shader_parameter("albedo", albedo)
func _on_set_texture_albedo(value : Texture):
texture_albedo = value
if _material != null:
_material.set_shader_parameter("texture_albedo", texture_albedo)
func _on_set_texture_frames(value : Vector2i):
texture_frames = value
if texture_frames.x <= 0:
texture_frames.x = 1
if texture_frames.y <= 0:
texture_frames.y = 1
if _material != null:
_material.set_shader_parameter("texture_frames", Vector2(texture_frames.x, texture_frames.y))
func _on_set_light_mode(value : int):
light_mode = value
if _material == null:
return
if _update_material_shader():
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
if multimesh.mesh != null:
for isur in range(multimesh.mesh.get_surface_count()):
multimesh.mesh.surface_set_material(isur, _material)
update_all_material()
_material.set_shader_parameter("light_mode", light_mode)
func _on_set_texture_filter(value : int) -> void:
texture_filter = value
if _material == null:
return
if _update_material_shader():
if multimesh == null:
multimesh = MultiMesh.new()
multimesh.mesh = mesh if mesh != null else _default_mesh
if multimesh.mesh != null:
for isur in range(multimesh.mesh.get_surface_count()):
multimesh.mesh.surface_set_material(isur, _material)
update_all_material()
func _on_set_texture_normal(value : Texture):
texture_normal = value
if _material != null:
_material.set_shader_parameter("texture_normal", texture_normal)
func _on_set_normal_scale(value : float):
normal_scale = value
if _material != null:
_material.set_shader_parameter("normal_scale", normal_scale)
func _on_set_texture_metallic(value : Texture):
texture_metallic = value
if _material != null:
_material.set_shader_parameter("texture_metallic", texture_metallic)
func _on_set_metallic_texture_channel(value : int):
metallic_texture_channel = value
if _material != null:
var channel : Vector4
if value == 0:
channel = Vector4(1,0,0,0)
elif value == 1:
channel = Vector4(0,1,0,0)
elif value == 2:
channel = Vector4(0,0,1,0)
elif value == 3:
channel = Vector4(0,0,0,1)
elif value == 4:
channel = Vector4(1,1,1,1)
_material.set_shader_parameter("metallic_texture_channel", channel)
func _on_set_metallic(value : float):
metallic = value
if _material != null:
_material.set_shader_parameter("metallic", metallic)
func _on_set_specular(value : float):
specular = value
if _material != null:
_material.set_shader_parameter("specular", specular)
func _on_set_texture_roughness(value : Texture):
texture_roughness = value
if _material != null:
_material.set_shader_parameter("texture_roughness", texture_roughness)
func _on_set_roughness_texture_channel(value : int):
roughness_texture_channel = value
if _material != null:
var channel : Vector4
if value == 0:
channel = Vector4(1,0,0,0)
elif value == 1:
channel = Vector4(0,1,0,0)
elif value == 2:
channel = Vector4(0,0,1,0)
elif value == 3:
channel = Vector4(0,0,0,1)
elif value == 4:
channel = Vector4(1,1,1,1)
_material.set_shader_parameter("roughness_texture_channel", channel)
func _on_set_roughness(value : float):
roughness = value
if _material != null:
_material.set_shader_parameter("roughness", roughness)
func _on_set_alpha_scissor_threshold(value : float):
alpha_scissor_threshold = value
if _material != null:
_material.set_shader_parameter("alpha_scissor_threshold", alpha_scissor_threshold)
func _on_set_scale_h(value : float):
scale_h = value
if _material != null:
_material.set_shader_parameter("scale_h", scale_h)
func _on_set_scale_w(value : float):
scale_w = value
if _material != null:
_material.set_shader_parameter("scale_w", scale_w)
func _on_set_scale_var(value : float):
scale_var = value
if _material != null:
_material.set_shader_parameter("scale_var", scale_var)
func _on_set_grass_strength(value : float):
grass_strength = value
if _material != null:
_material.set_shader_parameter("grass_strength", grass_strength)
func _on_set_interactive(value : bool):
if interactive == value:
return
interactive = value
if Engine.is_editor_hint():
return
if interactive:
_update_height_map()
else:
if _node_height_map != null:
_node_height_map.queue_free()
_node_height_map = null
if _material != null:
_material.set_shader_parameter("interactive_mode", interactive)
func _on_set_interactive_level_xz(value : float):
interactive_level_xz = value
if _material != null:
_material.set_shader_parameter("interactive_level_xz", interactive_level_xz)
func _on_set_interactive_level_y(value : float):
interactive_level_y = value
if _material != null:
_material.set_shader_parameter("interactive_level_y", interactive_level_y)
func _on_set_disable_node_scale(value : bool) -> void:
disable_node_scale = value
if disable_node_scale:
scale = Vector3.ONE
set_disable_scale(disable_node_scale)
func _on_set_disable_node_rotation(value : bool) -> void:
disable_node_rotation = value
if disable_node_rotation:
var prev_scale := scale
global_rotation = Vector3.ZERO
scale = prev_scale
set_notify_transform(true)
func _on_set_optimization_by_distance(value : bool):
optimization_by_distance = value
if _material != null:
_material.set_shader_parameter("optimization_by_distance", optimization_by_distance)
func _on_set_optimization_level(value : float):
optimization_level = value
if _material != null:
_material.set_shader_parameter("optimization_level", optimization_level)
func _on_set_optimization_dist_min(value : float):
optimization_dist_min = value
if _material != null:
_material.set_shader_parameter("optimization_dist_min", optimization_dist_min)
func _on_set_optimization_dist_max(value : float):
optimization_dist_max = value
if _material != null:
_material.set_shader_parameter("optimization_dist_max", optimization_dist_max)
func _on_set_player_pos(value : Vector3):
player_pos = Vector3(1000000, 1000000, 1000000)
if value != Vector3(1000000, 1000000, 1000000):
#_singleton.set_player_position(value)
if _wrng_deprec_playerpos and (Engine.is_editor_hint() or OS.is_debug_build()):
_wrng_deprec_playerpos = false
push_warning("Simple Grass Textured: ("+name+") player_pos parameter is deprecated, use SimpleGrass.set_player_position")
func _on_set_player_radius(value : float):
player_radius = 0.5
if _wrng_deprec_playerrad and (Engine.is_editor_hint() or OS.is_debug_build()):
_wrng_deprec_playerrad = false
push_warning("Simple Grass Textured: ("+name+") player_radius parameter is deprecated")
func _on_set_wind_dir(value : Vector3):
wind_dir = Vector3.RIGHT
#_singleton.wind_direction = value
if _wrng_deprec_windir and (Engine.is_editor_hint() or OS.is_debug_build()):
_wrng_deprec_windir = false
push_warning("Simple Grass Textured: ("+name+") wind_dir parameter is deprecated, use SimpleGrass.wind_direction")
func _on_set_wind_strength(value : float):
wind_strength = 0.15
#_singleton.wind_strength = value
if _wrng_deprec_windstrng and (Engine.is_editor_hint() or OS.is_debug_build()):
_wrng_deprec_windstrng = false
push_warning("Simple Grass Textured: ("+name+") wind_strength parameter is deprecated, use SimpleGrass.wind_strength")
func _on_set_wind_turbulence(value : float):
wind_turbulence = 1.0
#_singleton.wind_turbulence = value
if _wrng_deprec_windturb and (Engine.is_editor_hint() or OS.is_debug_build()):
_wrng_deprec_windturb = false
push_warning("Simple Grass Textured: ("+name+") wind_turbulence parameter is deprecated, use SimpleGrass.wind_turbulence")
func _on_set_wind_pattern(value : Texture):
wind_pattern = null
#RenderingServer.global_shader_parameter_set("sgt_wind_pattern", value)
if value != null and _wrng_deprec_windpatt and (Engine.is_editor_hint() or OS.is_debug_build()):
_wrng_deprec_windpatt = false
push_warning("Simple Grass Textured: ("+name+") wind_pattern parameter is deprecated, use SimpleGrass.set_wind_pattern")

View File

@ -0,0 +1 @@
uid://2juaclm8gc1n

View File

@ -0,0 +1,86 @@
# about.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends AcceptDialog
@export_multiline var message := ""
var icon_itch := "PHN2ZyBoZWlnaHQ9IjMyMy45NiIgd2lkdGg9IjMyMy45NiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCBmaWxsPSIjZmE1YzVjIiBoZWlnaHQ9IjMxMy45NiIgcnk9IjguNjE0MSIgc3Ryb2tlPSIjZDU0YzRjIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMTAiIHdpZHRoPSIzMTMuOTYiIHg9IjUiIHk9IjUiLz48ZyBmaWxsPSIjZmZmIiB0cmFuc2Zvcm09Im1hdHJpeCguOTM3MjEgMCAwIC45MzcyMSA1OC43NTEgMzUuMzM2KSI+PHBhdGggZD0ibS40MzUgMTk3LjI1aDI0LjE0MnY3MS41NTRoLTI0LjE0di03MS41NTZ6bTEyLjIxNy01LjgxN2MtNC4wNyAwLTcuMTk3LTEuMDItOS4zOC0zLjA1NC0yLjE4LTIuMDM2LTMuMjcyLTQuNzUtMy4yNzItOC4xNDUgMC0zLjIgMS4wOS01LjkxNiAzLjI3Mi04LjE0NSAyLjE4My0yLjIzIDUuMzEtMy4zNDUgOS4zOC0zLjM0NSA0LjA3MyAwIDcuMiAxLjExNSA5LjM4IDMuMzQ1IDIuMTgzIDIuMjMgMy4yNzQgNC45NDUgMy4yNzQgOC4xNDQgMCAzLjM5My0xLjA5IDYuMTA4LTMuMjcyIDguMTQ0LTIuMTggMi4wMzUtNS4zMDggMy4wNTQtOS4zOCAzLjA1NG00Ny4xMSA3OC44MTRjLTYuOTggMC0xMi4yNC0xLjg2Ni0xNS43OC01LjYtMy41NC0zLjczMi01LjMxLTguOTQ0LTUuMzEtMTUuNjM0di0zMy44ODRoLTkuNTk2di0xNy44OWg5LjU5N3YtMTcuNDVoMjQuMTQzdjE3LjQ1aDE1LjcwN3YxNy44OWgtMTUuNzF2MzAuMzk2YzAgMi4yMy40NiAzLjgwNiAxLjM4MiA0LjcyOHMyLjM1IDEuMzggNC4yOSAxLjM4YzIuNjE4IDAgNS40NzgtMS4xNjIgOC41OC0zLjQ5bDUuNTI4IDE1Ljg1M2MtMi44MSAxLjk0LTYuMDEgMy40NjgtOS42IDQuNTgzcy04IDEuNjcyLTEzLjIzNCAxLjY3Mm01OC40MyAwYy0xMS4wNTMgMC0xOS43NTUtMy4xNS0yNi4xMDUtOS40NTQtNi4zNTMtNi4zLTkuNTI4LTE1LjQ2NC05LjUyOC0yNy40ODUgMC04LjcyNiAxLjc5NC0xNi4wNDcgNS4zOC0yMS45NiAzLjY4NS02LjExIDguMjkyLTEwLjIzIDEzLjgyLTEyLjM2NCA1LjgxNS0yLjIzIDExLjE5Ny0zLjM0NCAxNi4xNDItMy4zNDQgNi4yMDMgMCAxMS4zOTIgMS4yMzcgMTUuNTYgMy43MSA0LjE3IDIuNDcyIDcuMjQ4IDUuMTYyIDkuMjM2IDguMDdzMy40MTcgNS4xODggNC4yOSA2LjgzN2wtMTYuNzI1IDEwLjc2Yy0xLjU1LTMuMTk4LTMuMjQ4LTUuNzItNS4wOS03LjU2Mi0xLjg0My0xLjg0LTQuMDczLTIuNzYtNi42OS0yLjc2LTMuNjg1IDAtNi41OTMgMS40NTMtOC43MjYgNC4zNi0yLjEzNCAyLjkxLTMuMiA3LjMyLTMuMiAxMy4yMzYgMCA2LjQgMS4yNiAxMS4xMDIgMy43OCAxNC4xMDggMi41MiAzLjAwNSA2LjExIDQuNTA3IDEwLjc2MiA0LjUwNyAzLjY4NCAwIDYuNzYzLS42NzYgOS4yMzctMi4wMzUgMi40Ny0xLjM1NiA0LjgyLTMuMSA3LjA1LTUuMjM1bDcuNzEgMTguMDM0Yy0yLjMyOCAxLjk0LTUuNzcgMy44NTUtMTAuMzI1IDUuNzQ1LTQuNTYgMS44OS0xMC4wODUgMi44MzUtMTYuNTggMi44MzVtMzQuNDctMS40NHYtOTcuMTVoMjQuMjg3djI5LjUyM2MyLjAzNy0xLjQ1NSA0LjUxLTIuNzQgNy40MTctMy44NTQgMi45MS0xLjExNSA2LjQtMS42NzMgMTAuNDczLTEuNjczIDguNTMgMCAxNC45MDcgMi40MjUgMTkuMTI1IDcuMjczczYuMzI2IDExLjY4MyA2LjMyNiAyMC41MDZ2NDUuMzc1aC0yNC4yODZ2LTQyLjQ2N2MwLTQuMDcyLS43NzUtNy4wNTQtMi4zMjctOC45NDRzLTMuNTktMi44MzYtNi4xMS0yLjgzNmMtMi4xMzIgMC00LjEyLjQ4NS01Ljk2MiAxLjQ1NS0xLjg0Ljk2OC0zLjM5NCAxLjk4Ni00LjY1NCAzLjA1MnY0OS43NGgtMjQuMjg2eiIvPjxnIHRyYW5zZm9ybT0ibWF0cml4KDEgLjIxNjI1IDAgMSAyLjI0MzIgLTM4OC4yOSkiPjxwYXRoIGQ9Im0xNjAuMTYgMzUzLjljLTEuMDI3NC0uMDQzMi0yLjE1NzIuMTQ4NTYtMy4zNDU3LjYwMTU2bC0xMDcuMzYgNDAuOTJjLTQuNzU0MiAxLjgxMi04LjU4MiA3LjExMjYtOC41ODIgMTEuODg1djEwNy43N2MtLjAwMDAwMSA0Ljc3MjIgMy44Mjc4IDcuMTUzOCA4LjU4MiA1LjM0MThsLjc5Mjk3LS4zMDI3M2MtLjE5NDUzLS42NDY4OS0uMzA4NTktMS4zNTg2LS4zMDg1OS0yLjE0MjZ2LTEwNy43N2MwLTQuNzcyMiAzLjg0My0xMC4wNzYgOC42MTUyLTExLjg5MWwxMDYuNTctNDAuNTJjLS42Njk0OC0yLjM2MDYtMi41MTctMy43OTAxLTQuOTU1MS0zLjg5MjZ6IiBmaWxsLW9wYWNpdHk9Ii4yMzQ1NyIvPjxwYXRoIGQ9Im01OC41NDkgMzk4LjE5Yy00Ljc3MjIgMS44MTQ2LTguNjE1MiA3LjExOS04LjYxNTIgMTEuODkxdjEwNy43N2MwIDQuNzcyMiAzLjg0MyA3LjE1MiA4LjYxNTIgNS4zMzc0bDEwNy43Ny00MC45OGM0Ljc3MjItMS44MTQ2IDguNjEzMy03LjExNjIgOC42MTMzLTExLjg4OHYtMTA3Ljc3YzAtNC43NzIyLTMuODQxMS03LjE1NDctOC42MTMzLTUuMzRsLTEwNy43NyA0MC45OHptMzAuNDYxIDIzLjE1NC4wMDgtLjAwM2M0Ljk1NzQtMS44ODUgOC40ODI5LS4wMTM0IDkuNzMyNCA0LjM1bDI3LjQtMTAuNDE5YzEuMjQ3My01LjMxMjggNC43NTA5LTkuODU3MyA5LjcwOS0xMS43NDMgNy44NzQ4LTIuOTk0NCAxNi41MzIgMS43OTk4IDE5LjIzNCAxMC42NTNsNi40NTcgMjEuMTM4YzIuNzAzNCA4Ljg1MjgtMS41MzUzIDE4LjU1MS05LjQxMDIgMjEuNTQ1LTYuOTEzMiAyLjYyODctMTQuNDI3LS43NDI2MS0xNy45ODgtNy41OTU1bC00My40MzYgMTYuNTE2Yy0zLjU2MDMgOS41NjA4LTExLjA3NiAxOC42NDctMTcuOTkgMjEuMjc2LTcuODc2MyAyLjk5NS0xMi4xMTItMy40ODA1LTkuNDA4Mi0xNC4zODlsNi40NTctMjYuMDQ5YzIuNzAyNy0xMC45MSAxMS4zNTgtMjIuMjg2IDE5LjIzNC0yNS4yODF6bTE3LjAzNSA3LjU1OTV2OC44MDY2bC03LjA3MDMgMi42ODg1IDYuNzMwNSA1Ljk3OTggNi43MjY2IDUuOTgxMyA2LjczNDQtMTEuMTA0IDYuNzMwNS0xMS4wOTgtNi42MzA5IDIuNTIxNHYtOC44MDI3bC0xMy4yMjEgNS4wMjcyeiIvPjwvZz48L2c+PC9zdmc+"
var icon_patreon_dark := "PHN2ZyB2aWV3Qm94PSIwIDAgMjM0Ljg5IDMzNyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBkPSJtMCAwaDIzNC44OXYyNTcuMjZoLTIzNC44OXoiLz48L2NsaXBQYXRoPjxwYXRoIGQ9Im0wIDMwNy41NWMwLTEuMzggMS4wMi0yLjMxIDIuNjItMi4zMWg3LjY0YzYuOTMgMCAxMS42NCAzLjgyIDExLjY0IDkuNDJzLTQuNjYgOS41OS0xMS42NCA5LjU5aC0xLjA3Yy0yLjY2IDAtNC4wNCAxLjM4LTQuMDQgMy42djYuMjZjMCAxLjc4LTEuMDIgMi44OS0yLjU4IDIuODlzLTIuNTctMS4xMS0yLjU3LTIuODl6bTUuMTUgNy45NWMwIDIuOCAxLjQyIDQuMTMgNC4xMyA0LjEzaC42N2MzLjgyIDAgNi42Mi0xLjU1IDYuNjItNC45N3MtMi44LTQuOTgtNi42Mi00Ljk4aC0uNjdjLTIuNzEgMC00LjEzIDEuMzMtNC4xMyA0LjEzem0xNi43NSAxOS4wNmMwIDEuNDcgMS4wMiAyLjQ0IDIuNTggMi40NCAxLjAyIDAgMS45NS0uNjcgMi40OS0yLjA0bC45My0yLjQ5Yy43Ni0yIDIuMDktMi45MyAzLjU1LTIuOTNoOC40NGMxLjQ3IDAgMi44LjkzIDMuNTUgMi45M2wuOTMgMi40OWMuNTMgMS4zOCAxLjQ3IDIuMDQgMi40OSAyLjA0IDEuNTUgMCAyLjU4LS45OCAyLjU4LTIuNDQgMC0uNC0uMDktLjg5LS4yNy0xLjM4bC0xMC4wOC0yNi4yYy0uNjItMS42LTIuMDQtMi40LTMuNDItMi40cy0yLjguOC0zLjQyIDIuNGwtMTAuMDggMjYuMmMtLjE4LjQ5LS4yNy45OC0uMjcgMS4zOHptOS44MS0xMS45MWMwLS40OS4xMy0uOTMuMzYtMS42bDEuOTEtNS4yOWMuMzYtMS4wMi45OC0xLjUxIDEuNjktMS41MXMxLjMzLjQ5IDEuNjkgMS41MWwxLjkxIDUuMjljLjIyLjY3LjM2IDEuMTEuMzYgMS42IDAgMS4zMy0uNzYgMi4yNy0yLjc1IDIuMjdoLTIuNGMtMiAwLTIuNzUtLjkzLTIuNzUtMi4yN3ptMTYuMDQtMTUuMDFjMC0xLjQyIDEuMDItMi40IDIuNjctMi40aDIwLjQzYzEuNjQgMCAyLjY3Ljk4IDIuNjcgMi40cy0xLjAyIDIuNC0yLjY3IDIuNGgtMy40MmMtMi43MSAwLTQuMTggMS4zOC00LjE4IDQuNTN2MTkuNWMwIDEuODItMS4wMiAyLjkzLTIuNjIgMi45M3MtMi42Mi0xLjExLTIuNjItMi45M3YtMTkuNWMwLTMuMTUtMS40Ny00LjUzLTQuMTgtNC41M2gtMy40MmMtMS42NCAwLTIuNjctLjk4LTIuNjctMi40em0zMC41NiAyNi40N2MwIDEuNzggMS4wMiAyLjg5IDIuNTggMi44OXMyLjU4LTEuMTEgMi41OC0yLjg5di03LjA2YzAtMiAxLjE1LTIuODQgMi41OC0yLjg0aC4zNmMuOTMgMCAxLjg3LjU4IDIuNDQgMS40Mmw2Ljc1IDkuOWMuNjIuOTMgMS40MiAxLjQ3IDIuNDQgMS40NyAxLjMzIDAgMi40LTEuMTEgMi40LTIuNDQgMC0uNTMtLjE4LTEuMTEtLjU4LTEuNjlsLTQuNDktNi4zMWMtLjUzLS43Ni0uNzUtMS4zOC0uNzUtMS45MSAwLTEuMTEuOTgtMS45MSAyLjEzLTIuNzUgMi4wOS0xLjU1IDQuMzUtMy42IDQuMzUtNy41MSAwLTUuNDYtNC4yNi05LjE1LTExLjI4LTkuMTVoLTguOTNjLTEuNiAwLTIuNTguOTMtMi41OCAyLjMxem01LjE1LTE5LjAxdi0xLjMzYzAtMi44OSAxLjUxLTQuMDkgMy44Mi00LjA5aDIuMjJjMy44MiAwIDYuMjYgMS40MiA2LjI2IDQuNzFzLTIuNTggNC44LTYuNCA0LjhoLTIuMDljLTIuMzEgMC0zLjgyLTEuMi0zLjgyLTQuMDl6bTI0LjIxIDE4Ljkydi0yNi40N2MwLTEuMzguOTgtMi4zMSAyLjU4LTIuMzFoMTYuNzljMS42IDAgMi41OC45MyAyLjU4IDIuMzFzLS45OCAyLjMxLTIuNTggMi4zMWgtMTAuNjJjLTIuMDkgMC0zLjYgMS4yNC0zLjYgMy42di45OGMwIDIuMzUgMS41MSAzLjYgMy42IDMuNmg4LjIyYzEuNiAwIDIuNTguOTMgMi41OCAyLjMxcy0uOTggMi4zMS0yLjU4IDIuMzFoLTcuOTFjLTIuMDkgMC0zLjkxIDEuMjktMy45MSAzLjkxdjEuMjRjMCAyLjYyIDEuODIgMy45MSAzLjkxIDMuOTFoMTAuMzFjMS42IDAgMi41OC45MyAyLjU4IDIuMzFzLS45OCAyLjMxLTIuNTggMi4zMWgtMTYuNzljLTEuNiAwLTIuNTgtLjkzLTIuNTgtMi4zMXptMjUtMTMuMjNjMC05LjU5IDcuMi0xNi4yMSAxNS42My0xNi4yMXMxNS42MyA2LjYyIDE1LjYzIDE2LjIxLTcuMiAxNi4yMS0xNS42MyAxNi4yMS0xNS42My02LjYyLTE1LjYzLTE2LjIxem01Ljg3IDBjMCA2Ljc1IDQgMTEuMDYgOS43NyAxMS4wNnM5Ljc3LTQuMzEgOS43Ny0xMS4wNi00LTExLjA2LTkuNzctMTEuMDYtOS43NyA0LjI2LTkuNzcgMTEuMDZ6bTMxLjU4IDEzLjMyYzAgMS43OCAxLjAyIDIuODkgMi41OCAyLjg5czIuNTgtMS4xMSAyLjU4LTIuODl2LTEzLjU5YzAtMS42NC45OC0yLjQ0IDItMi40NC44IDAgMS40Ni40NCAxLjk1IDEuMjRsOC41MyAxNC4yNmMxLjE1IDEuOTUgMi4yMiAzLjQyIDQuNCAzLjQyczMuNi0xLjUxIDMuNi0zLjk1di0yNS41OGMwLTEuNzgtMS4wMi0yLjg5LTIuNTgtMi44OXMtMi41OCAxLjExLTIuNTggMi44OXYxMy41OWMwIDEuNjQtLjk4IDIuNDQtMiAyLjQ0LS44IDAtMS40Ny0uNDQtMS45NS0xLjI0bC04LjUzLTE0LjI2Yy0xLjE1LTEuOTUtMi4yMi0zLjQyLTQuNC0zLjQycy0zLjYgMS41MS0zLjYgMy45NXoiIGZpbGw9IiMwMTAxMDEiLz48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxwYXRoIGQ9Im0yMzQuODkgNzcuMjljLS4wNS0zMi44NS0yNS42My01OS43Ny01NS42NC02OS40OC0zNy4yOC0xMi4wNy04Ni40My0xMC4zMi0xMjIuMDMgNi40Ny00My4xNCAyMC4zNi01Ni42OSA2NC45NS01Ny4xOSAxMDkuNDItLjQxIDM2LjU2IDMuMjMgMTMyLjg3IDU3LjU1IDEzMy41NSA0MC4zNi41MSA0Ni4zNy01MS40OSA2NS4wNS03Ni41NCAxMy4yOS0xNy44MiAzMC4zOS0yMi44NSA1MS40NS0yOC4wNyAzNi4yLTguOTYgNjAuODYtMzcuNTIgNjAuODEtNzUuMzZ6IiBmaWxsPSIjMDEwMTAxIi8+PC9nPjwvc3ZnPg=="
var icon_patreon_light := "PHN2ZyB2aWV3Qm94PSIwIDAgMjM0Ljg5IDMzNyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGNsaXBQYXRoIGlkPSJhIj48cGF0aCBkPSJtMCAwaDIzNC44OXYyNTcuMjZoLTIzNC44OXoiLz48L2NsaXBQYXRoPjxwYXRoIGQ9Im0wIDMwNy41NWMwLTEuMzggMS4wMi0yLjMxIDIuNjItMi4zMWg3LjY0YzYuOTMgMCAxMS42NCAzLjgyIDExLjY0IDkuNDJzLTQuNjYgOS41OS0xMS42NCA5LjU5aC0xLjA3Yy0yLjY2IDAtNC4wNCAxLjM4LTQuMDQgMy42djYuMjZjMCAxLjc4LTEuMDIgMi44OS0yLjU4IDIuODlzLTIuNTctMS4xMS0yLjU3LTIuODl6bTUuMTUgNy45NWMwIDIuOCAxLjQyIDQuMTMgNC4xMyA0LjEzaC42N2MzLjgyIDAgNi42Mi0xLjU1IDYuNjItNC45N3MtMi44LTQuOTgtNi42Mi00Ljk4aC0uNjdjLTIuNzEgMC00LjEzIDEuMzMtNC4xMyA0LjEzem0xNi43NSAxOS4wNmMwIDEuNDcgMS4wMiAyLjQ0IDIuNTggMi40NCAxLjAyIDAgMS45NS0uNjcgMi40OS0yLjA0bC45My0yLjQ5Yy43Ni0yIDIuMDktMi45MyAzLjU1LTIuOTNoOC40NGMxLjQ3IDAgMi44LjkzIDMuNTUgMi45M2wuOTMgMi40OWMuNTMgMS4zOCAxLjQ3IDIuMDQgMi40OSAyLjA0IDEuNTUgMCAyLjU4LS45OCAyLjU4LTIuNDQgMC0uNC0uMDktLjg5LS4yNy0xLjM4bC0xMC4wOC0yNi4yYy0uNjItMS42LTIuMDQtMi40LTMuNDItMi40cy0yLjguOC0zLjQyIDIuNGwtMTAuMDggMjYuMmMtLjE4LjQ5LS4yNy45OC0uMjcgMS4zOHptOS44MS0xMS45MWMwLS40OS4xMy0uOTMuMzYtMS42bDEuOTEtNS4yOWMuMzYtMS4wMi45OC0xLjUxIDEuNjktMS41MXMxLjMzLjQ5IDEuNjkgMS41MWwxLjkxIDUuMjljLjIyLjY3LjM2IDEuMTEuMzYgMS42IDAgMS4zMy0uNzYgMi4yNy0yLjc1IDIuMjdoLTIuNGMtMiAwLTIuNzUtLjkzLTIuNzUtMi4yN3ptMTYuMDQtMTUuMDFjMC0xLjQyIDEuMDItMi40IDIuNjctMi40aDIwLjQzYzEuNjQgMCAyLjY3Ljk4IDIuNjcgMi40cy0xLjAyIDIuNC0yLjY3IDIuNGgtMy40MmMtMi43MSAwLTQuMTggMS4zOC00LjE4IDQuNTN2MTkuNWMwIDEuODItMS4wMiAyLjkzLTIuNjIgMi45M3MtMi42Mi0xLjExLTIuNjItMi45M3YtMTkuNWMwLTMuMTUtMS40Ny00LjUzLTQuMTgtNC41M2gtMy40MmMtMS42NCAwLTIuNjctLjk4LTIuNjctMi40em0zMC41NiAyNi40N2MwIDEuNzggMS4wMiAyLjg5IDIuNTggMi44OXMyLjU4LTEuMTEgMi41OC0yLjg5di03LjA2YzAtMiAxLjE1LTIuODQgMi41OC0yLjg0aC4zNmMuOTMgMCAxLjg3LjU4IDIuNDQgMS40Mmw2Ljc1IDkuOWMuNjIuOTMgMS40MiAxLjQ3IDIuNDQgMS40NyAxLjMzIDAgMi40LTEuMTEgMi40LTIuNDQgMC0uNTMtLjE4LTEuMTEtLjU4LTEuNjlsLTQuNDktNi4zMWMtLjUzLS43Ni0uNzUtMS4zOC0uNzUtMS45MSAwLTEuMTEuOTgtMS45MSAyLjEzLTIuNzUgMi4wOS0xLjU1IDQuMzUtMy42IDQuMzUtNy41MSAwLTUuNDYtNC4yNi05LjE1LTExLjI4LTkuMTVoLTguOTNjLTEuNiAwLTIuNTguOTMtMi41OCAyLjMxem01LjE1LTE5LjAxdi0xLjMzYzAtMi44OSAxLjUxLTQuMDkgMy44Mi00LjA5aDIuMjJjMy44MiAwIDYuMjYgMS40MiA2LjI2IDQuNzFzLTIuNTggNC44LTYuNCA0LjhoLTIuMDljLTIuMzEgMC0zLjgyLTEuMi0zLjgyLTQuMDl6bTI0LjIxIDE4Ljkydi0yNi40N2MwLTEuMzguOTgtMi4zMSAyLjU4LTIuMzFoMTYuNzljMS42IDAgMi41OC45MyAyLjU4IDIuMzFzLS45OCAyLjMxLTIuNTggMi4zMWgtMTAuNjJjLTIuMDkgMC0zLjYgMS4yNC0zLjYgMy42di45OGMwIDIuMzUgMS41MSAzLjYgMy42IDMuNmg4LjIyYzEuNiAwIDIuNTguOTMgMi41OCAyLjMxcy0uOTggMi4zMS0yLjU4IDIuMzFoLTcuOTFjLTIuMDkgMC0zLjkxIDEuMjktMy45MSAzLjkxdjEuMjRjMCAyLjYyIDEuODIgMy45MSAzLjkxIDMuOTFoMTAuMzFjMS42IDAgMi41OC45MyAyLjU4IDIuMzFzLS45OCAyLjMxLTIuNTggMi4zMWgtMTYuNzljLTEuNiAwLTIuNTgtLjkzLTIuNTgtMi4zMXptMjUtMTMuMjNjMC05LjU5IDcuMi0xNi4yMSAxNS42My0xNi4yMXMxNS42MyA2LjYyIDE1LjYzIDE2LjIxLTcuMiAxNi4yMS0xNS42MyAxNi4yMS0xNS42My02LjYyLTE1LjYzLTE2LjIxem01Ljg3IDBjMCA2Ljc1IDQgMTEuMDYgOS43NyAxMS4wNnM5Ljc3LTQuMzEgOS43Ny0xMS4wNi00LTExLjA2LTkuNzctMTEuMDYtOS43NyA0LjI2LTkuNzcgMTEuMDZ6bTMxLjU4IDEzLjMyYzAgMS43OCAxLjAyIDIuODkgMi41OCAyLjg5czIuNTgtMS4xMSAyLjU4LTIuODl2LTEzLjU5YzAtMS42NC45OC0yLjQ0IDItMi40NC44IDAgMS40Ni40NCAxLjk1IDEuMjRsOC41MyAxNC4yNmMxLjE1IDEuOTUgMi4yMiAzLjQyIDQuNCAzLjQyczMuNi0xLjUxIDMuNi0zLjk1di0yNS41OGMwLTEuNzgtMS4wMi0yLjg5LTIuNTgtMi44OXMtMi41OCAxLjExLTIuNTggMi44OXYxMy41OWMwIDEuNjQtLjk4IDIuNDQtMiAyLjQ0LS44IDAtMS40Ny0uNDQtMS45NS0xLjI0bC04LjUzLTE0LjI2Yy0xLjE1LTEuOTUtMi4yMi0zLjQyLTQuNC0zLjQycy0zLjYgMS41MS0zLjYgMy45NXoiIGZpbGw9IiNmZmYiLz48ZyBjbGlwLXBhdGg9InVybCgjYSkiPjxwYXRoIGQ9Im0yMzQuODkgNzcuMjljLS4wNS0zMi44NS0yNS42My01OS43Ny01NS42NC02OS40OC0zNy4yOC0xMi4wNy04Ni40My0xMC4zMi0xMjIuMDMgNi40Ny00My4xNCAyMC4zNi01Ni42OSA2NC45NS01Ny4xOSAxMDkuNDItLjQxIDM2LjU2IDMuMjMgMTMyLjg3IDU3LjU1IDEzMy41NSA0MC4zNi41MSA0Ni4zNy01MS40OSA2NS4wNS03Ni41NCAxMy4yOS0xNy44MiAzMC4zOS0yMi44NSA1MS40NS0yOC4wNyAzNi4yLTguOTYgNjAuODYtMzcuNTIgNjAuODEtNzUuMzZ6IiBmaWxsPSIjZmZmIi8+PC9nPjwvc3ZnPg=="
var icon_github_dark := "PHN2ZyBoZWlnaHQ9Ijk2IiB3aWR0aD0iOTgiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggY2xpcC1ydWxlPSJldmVub2RkIiBkPSJtNDguODU0IDBjLTI3LjAxNSAwLTQ4Ljg1NCAyMi00OC44NTQgNDkuMjE3IDAgMjEuNzU2IDEzLjk5MyA0MC4xNzIgMzMuNDA1IDQ2LjY5IDIuNDI3LjQ5IDMuMzE2LTEuMDU5IDMuMzE2LTIuMzYyIDAtMS4xNDEtLjA4LTUuMDUyLS4wOC05LjEyNy0xMy41OSAyLjkzNC0xNi40Mi01Ljg2Ny0xNi40Mi01Ljg2Ny0yLjE4NC01LjcwNC01LjQyLTcuMTctNS40Mi03LjE3LTQuNDQ4LTMuMDE1LjMyNC0zLjAxNS4zMjQtMy4wMTUgNC45MzQuMzI2IDcuNTIzIDUuMDUyIDcuNTIzIDUuMDUyIDQuMzY3IDcuNDk2IDExLjQwNCA1LjM3OCAxNC4yMzUgNC4wNzQuNDA0LTMuMTc4IDEuNjk5LTUuMzc4IDMuMDc0LTYuNi0xMC44MzktMS4xNDEtMjIuMjQzLTUuMzc4LTIyLjI0My0yNC4yODMgMC01LjM3OCAxLjk0LTkuNzc4IDUuMDE0LTEzLjItLjQ4NS0xLjIyMi0yLjE4NC02LjI3NS40ODYtMTMuMDM4IDAgMCA0LjEyNS0xLjMwNCAxMy40MjYgNS4wNTJhNDYuOTcgNDYuOTcgMCAwIDEgMTIuMjE0LTEuNjNjNC4xMjUgMCA4LjMzLjU3MSAxMi4yMTMgMS42MyA5LjMwMi02LjM1NiAxMy40MjctNS4wNTIgMTMuNDI3LTUuMDUyIDIuNjcgNi43NjMuOTcgMTEuODE2LjQ4NSAxMy4wMzggMy4xNTUgMy40MjIgNS4wMTUgNy44MjIgNS4wMTUgMTMuMiAwIDE4LjkwNS0xMS40MDQgMjMuMDYtMjIuMzI0IDI0LjI4MyAxLjc4IDEuNTQ4IDMuMzE2IDQuNDgxIDMuMzE2IDkuMTI2IDAgNi42LS4wOCAxMS44OTctLjA4IDEzLjUyNiAwIDEuMzA0Ljg5IDIuODUzIDMuMzE2IDIuMzY0IDE5LjQxMi02LjUyIDMzLjQwNS0yNC45MzUgMzMuNDA1LTQ2LjY5MS4wOC0yNy4yMTctMjEuODM5LTQ5LjIxNy00OC43NzMtNDkuMjE3eiIgZmlsbD0iIzI0MjkyZiIgZmlsbC1ydWxlPSJldmVub2RkIi8+PC9zdmc+"
var icon_github_light := "PHN2ZyB3aWR0aD0iOTgiIGhlaWdodD0iOTYiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbC1ydWxlPSJldmVub2RkIiBjbGlwLXJ1bGU9ImV2ZW5vZGQiIGQ9Ik00OC44NTQgMEMyMS44MzkgMCAwIDIyIDAgNDkuMjE3YzAgMjEuNzU2IDEzLjk5MyA0MC4xNzIgMzMuNDA1IDQ2LjY5IDIuNDI3LjQ5IDMuMzE2LTEuMDU5IDMuMzE2LTIuMzYyIDAtMS4xNDEtLjA4LTUuMDUyLS4wOC05LjEyNy0xMy41OSAyLjkzNC0xNi40Mi01Ljg2Ny0xNi40Mi01Ljg2Ny0yLjE4NC01LjcwNC01LjQyLTcuMTctNS40Mi03LjE3LTQuNDQ4LTMuMDE1LjMyNC0zLjAxNS4zMjQtMy4wMTUgNC45MzQuMzI2IDcuNTIzIDUuMDUyIDcuNTIzIDUuMDUyIDQuMzY3IDcuNDk2IDExLjQwNCA1LjM3OCAxNC4yMzUgNC4wNzQuNDA0LTMuMTc4IDEuNjk5LTUuMzc4IDMuMDc0LTYuNi0xMC44MzktMS4xNDEtMjIuMjQzLTUuMzc4LTIyLjI0My0yNC4yODMgMC01LjM3OCAxLjk0LTkuNzc4IDUuMDE0LTEzLjItLjQ4NS0xLjIyMi0yLjE4NC02LjI3NS40ODYtMTMuMDM4IDAgMCA0LjEyNS0xLjMwNCAxMy40MjYgNS4wNTJhNDYuOTcgNDYuOTcgMCAwIDEgMTIuMjE0LTEuNjNjNC4xMjUgMCA4LjMzLjU3MSAxMi4yMTMgMS42MyA5LjMwMi02LjM1NiAxMy40MjctNS4wNTIgMTMuNDI3LTUuMDUyIDIuNjcgNi43NjMuOTcgMTEuODE2LjQ4NSAxMy4wMzggMy4xNTUgMy40MjIgNS4wMTUgNy44MjIgNS4wMTUgMTMuMiAwIDE4LjkwNS0xMS40MDQgMjMuMDYtMjIuMzI0IDI0LjI4MyAxLjc4IDEuNTQ4IDMuMzE2IDQuNDgxIDMuMzE2IDkuMTI2IDAgNi42LS4wOCAxMS44OTctLjA4IDEzLjUyNiAwIDEuMzA0Ljg5IDIuODUzIDMuMzE2IDIuMzY0IDE5LjQxMi02LjUyIDMzLjQwNS0yNC45MzUgMzMuNDA1LTQ2LjY5MUM5Ny43MDcgMjIgNzUuNzg4IDAgNDguODU0IDB6IiBmaWxsPSIjZmZmIi8+PC9zdmc+"
func _ready() -> void:
var config := ConfigFile.new()
config.load("res://addons/simplegrasstextured/plugin.cfg")
%RichTextLabel.text = message.format({"_version_num":config.get_value("plugin", "version")})
name = &"SimpleGrassTexturedHelpAbout"
get_ok_button().custom_minimum_size.x = 100
size.y = 250
await %ItchContainer.draw
var gui_h: int = max(32, %ItchContainer.size.y)
var base_color: Color = EditorInterface.get_base_control().get_theme_color(&"base_color", &"Editor")
if base_color.get_luminance() < 0.5:
_set_icon_button(%ItchContainer, icon_itch, gui_h, 324)
_set_icon_button(%PatreonContainer, icon_patreon_light, gui_h, 360)
_set_icon_button(%GithubContainer, icon_github_light, gui_h, 96)
else:
_set_icon_button(%ItchContainer, icon_itch, gui_h, 324)
_set_icon_button(%PatreonContainer, icon_patreon_dark, gui_h, 360)
_set_icon_button(%GithubContainer, icon_github_dark, gui_h, 96)
func _set_icon_button(container: Container, icon: String, gui_h: int, icon_size: int):
var image := Image.new()
var t_rect: TextureRect
image.load_svg_from_string(Marshalls.base64_to_utf8(icon), gui_h / float(icon_size))
t_rect = TextureRect.new()
t_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
t_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
t_rect.texture = ImageTexture.create_from_image(image)
t_rect.custom_minimum_size.x = gui_h
container.add_child(t_rect)
container.move_child(t_rect, 0)
func _on_canceled() -> void:
queue_free()
func _on_confirmed() -> void:
queue_free()
func _on_itch_button_pressed() -> void:
OS.shell_open("https://icterusgames.itch.io")
func _on_patreon_button_pressed() -> void:
OS.shell_open("https://www.patreon.com/IcterusGames")
func _on_github_button_pressed() -> void:
OS.shell_open("https://github.com/IcterusGames/SimpleGrassTextured")

View File

@ -0,0 +1 @@
uid://cu72rjuvdnnx

View File

@ -0,0 +1,187 @@
[gd_scene load_steps=7 format=3 uid="uid://cb8klhv6nwvtl"]
[ext_resource type="Script" uid="uid://cu72rjuvdnnx" path="res://addons/simplegrasstextured/gui/about.gd" id="1_r23hh"]
[ext_resource type="Material" uid="uid://cl74usvfymgvs" path="res://addons/simplegrasstextured/materials/icon.material" id="2_4glo1"]
[ext_resource type="Texture2D" uid="uid://cubr1a55brlfp" path="res://addons/simplegrasstextured/sgt_icon_48.svg" id="3_7w1su"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hnw4u"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_lwftf"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5fhmg"]
content_margin_left = 5.0
content_margin_top = 5.0
content_margin_right = 5.0
content_margin_bottom = 5.0
draw_center = false
[node name="SimpleGrassTexturedHelpAbout" type="AcceptDialog"]
title = "About"
position = Vector2i(0, 36)
size = Vector2i(644, 475)
visible = true
min_size = Vector2i(520, 200)
max_size = Vector2i(1280, 720)
script = ExtResource("1_r23hh")
message = "Simple Grass Textured Plugin
v. {_version_num}
by IcterusGames
[font_size=7] [/font_size]
[b]Support me on:[/b]"
[node name="MarginContainer" type="MarginContainer" parent="."]
offset_left = 8.0
offset_top = 8.0
offset_right = 636.0
offset_bottom = 426.0
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 20
[node name="TextureRect" type="TextureRect" parent="MarginContainer/HBoxContainer"]
material = ExtResource("2_4glo1")
custom_minimum_size = Vector2(48, 48)
layout_mode = 2
texture = ExtResource("3_7w1su")
stretch_mode = 4
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="RichTextLabel" type="RichTextLabel" parent="MarginContainer/HBoxContainer/VBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(420, 0)
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/focus = SubResource("StyleBoxEmpty_hnw4u")
theme_override_styles/normal = SubResource("StyleBoxEmpty_lwftf")
bbcode_enabled = true
text = "Simple Grass Textured Plugin
v. 2.0.8
by IcterusGames
[font_size=7] [/font_size]
[b]Support me on:[/b]"
fit_content = true
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/margin_left = 15
[node name="ItchButton" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="ItchContainer" type="HBoxContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer/MarginContainer"]
unique_name_in_owner = true
layout_mode = 2
mouse_filter = 2
[node name="RichTextLabel" type="RichTextLabel" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer/MarginContainer/ItchContainer"]
custom_minimum_size = Vector2(400, 0)
layout_mode = 2
size_flags_vertical = 4
mouse_filter = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_5fhmg")
bbcode_enabled = true
text = "[b]Itch.io[/b]
[url]https://icterusgames.itch.io[url]"
fit_content = true
[node name="MarginContainer2" type="MarginContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/margin_left = 15
[node name="PatreonButton" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer2"]
layout_mode = 2
size_flags_horizontal = 3
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer2"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="PatreonContainer" type="HBoxContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer2/MarginContainer"]
unique_name_in_owner = true
layout_mode = 2
mouse_filter = 2
[node name="RichTextLabel" type="RichTextLabel" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer2/MarginContainer/PatreonContainer"]
custom_minimum_size = Vector2(400, 0)
layout_mode = 2
size_flags_vertical = 4
mouse_filter = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_5fhmg")
bbcode_enabled = true
text = "[b]Patreon[/b]
[url]https://www.patreon.com/IcterusGames[/url]"
fit_content = true
[node name="RichTextLabel2" type="RichTextLabel" parent="MarginContainer/HBoxContainer/VBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(420, 0)
layout_mode = 2
size_flags_horizontal = 3
theme_override_styles/focus = SubResource("StyleBoxEmpty_hnw4u")
theme_override_styles/normal = SubResource("StyleBoxEmpty_lwftf")
bbcode_enabled = true
text = "[font_size=7] [/font_size]
[b]Source code on:[/b]"
fit_content = true
[node name="MarginContainer3" type="MarginContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/margin_left = 15
[node name="GithubButton" type="Button" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer3"]
layout_mode = 2
size_flags_horizontal = 3
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer3"]
layout_mode = 2
mouse_filter = 2
theme_override_constants/margin_left = 5
theme_override_constants/margin_top = 5
theme_override_constants/margin_right = 5
theme_override_constants/margin_bottom = 5
[node name="GithubContainer" type="HBoxContainer" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer3/MarginContainer"]
unique_name_in_owner = true
layout_mode = 2
mouse_filter = 2
[node name="RichTextLabel" type="RichTextLabel" parent="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer3/MarginContainer/GithubContainer"]
custom_minimum_size = Vector2(400, 0)
layout_mode = 2
size_flags_vertical = 4
mouse_filter = 2
theme_override_styles/normal = SubResource("StyleBoxFlat_5fhmg")
bbcode_enabled = true
text = "[b]Github[/b]
[url]https://github.com/IcterusGames/SimpleGrassTextured[/url]"
fit_content = true
[connection signal="canceled" from="." to="." method="_on_canceled"]
[connection signal="confirmed" from="." to="." method="_on_confirmed"]
[connection signal="pressed" from="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer/ItchButton" to="." method="_on_itch_button_pressed"]
[connection signal="pressed" from="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer2/PatreonButton" to="." method="_on_patreon_button_pressed"]
[connection signal="pressed" from="MarginContainer/HBoxContainer/VBoxContainer/MarginContainer3/GithubButton" to="." method="_on_github_button_pressed"]

View File

@ -0,0 +1,38 @@
# clear_all_confirmation_dialog.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends ConfirmationDialog
func _ready() -> void:
get_ok_button().custom_minimum_size.x = 100
get_cancel_button().custom_minimum_size.x = 100
func _on_confirmed() -> void:
queue_free()
func _on_canceled() -> void:
queue_free()

View File

@ -0,0 +1 @@
uid://wckg68rm05vd

View File

@ -0,0 +1,13 @@
[gd_scene load_steps=2 format=3 uid="uid://c1242d1bef7lb"]
[ext_resource type="Script" uid="uid://wckg68rm05vd" path="res://addons/simplegrasstextured/gui/clear_all_confirmation_dialog.gd" id="1_xk6u2"]
[node name="ClearAllConfirmationDialog" type="ConfirmationDialog"]
size = Vector2i(377, 109)
ok_button_text = "Yes"
dialog_text = "Are you sure you want to remove all the grass?"
cancel_button_text = "No"
script = ExtResource("1_xk6u2")
[connection signal="canceled" from="." to="." method="_on_canceled"]
[connection signal="confirmed" from="." to="." method="_on_confirmed"]

View File

@ -0,0 +1,309 @@
# domain_range.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2025 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends Control
signal value_changed(value_min: float, value_max: float)
@export var range_min: float = 0.0
@export var range_max: float = 180.0
@export var step: float = 1.0
@export var value_min: float = 0.0 : set = set_value_min
@export var value_max: float = 45.0 : set = set_value_max
var _ed_scale: float = 1.0
var _slider_area := Rect2()
var _slider_range := Rect2()
var _grabber_min_pos := Vector2.ZERO
var _grabber_max_pos := Vector2.ZERO
var _is_grabbing_min_grab := false
var _is_grabbing_max_grab := false
var _is_grabbing_min_value := false
var _is_grabbing_max_value := false
var _is_editing_min := false
var _is_editing_max := false
var _mouse_over := false
var _mouse_click_pos := Vector2.ONE * 100000
var _mouse_click_rel := Vector2.ZERO
var _mouse_click_value := 0.0
var _mouse_over_grabber_min := false
var _mouse_over_grabber_max := false
@onready var _line_edit: LineEdit = $LineEdit
func _ready() -> void:
theme_changed.connect(_on_theme_changed)
_on_theme_changed()
_update_tooltip()
func _notification(what: int) -> void:
if what == NOTIFICATION_MOUSE_ENTER_SELF:
_mouse_over = true
queue_redraw()
if what == NOTIFICATION_MOUSE_EXIT_SELF:
_mouse_over = false
_mouse_over_grabber_min = false
_mouse_over_grabber_max = false
queue_redraw()
func _gui_input(event: InputEvent) -> void:
if event is InputEventMouseMotion:
_mouse_over_grabber_min = false
_mouse_over_grabber_max = false
if _is_grabbing_max_value:
if event.position.distance_to(_mouse_click_pos) > 8 * _ed_scale:
_mouse_click_pos = Vector2.ONE * 100000
set_value_max(value_max + event.screen_relative.x * step)
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
elif _is_grabbing_min_value:
if event.position.distance_to(_mouse_click_pos) > 8 * _ed_scale:
_mouse_click_pos = Vector2.ONE * 100000
set_value_min(value_min + event.screen_relative.x * step)
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
elif _is_grabbing_max_grab:
_mouse_click_pos = Vector2.ONE * 100000
set_value_max((event.position.x - _mouse_click_rel.x - _slider_area.position.x) / _slider_area.size.x * range_max)
elif _is_grabbing_min_grab:
_mouse_click_pos = Vector2.ONE * 100000
set_value_min((event.position.x - _mouse_click_rel.x - _slider_area.position.x) / _slider_area.size.x * range_max)
if event.position.distance_to(_grabber_max_pos) <= 8 * _ed_scale:
_mouse_over_grabber_max = true
if event.position.distance_to(_grabber_min_pos) <= 8 * _ed_scale:
_mouse_over_grabber_min = true
queue_redraw()
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if event.pressed:
_mouse_click_pos = event.position
if (event.position.distance_to(_grabber_min_pos) <= 8 * _ed_scale and
event.position.distance_to(_grabber_max_pos) <= 8 * _ed_scale):
if event.position.x < (_grabber_min_pos.x + _grabber_max_pos.x) / 2:
_is_grabbing_min_grab = true
_mouse_click_rel = event.position - _grabber_min_pos
_mouse_click_value = value_min
else:
_is_grabbing_max_grab = true
_mouse_click_rel = event.position - _grabber_max_pos
_mouse_click_value = value_max
elif event.position.distance_to(_grabber_min_pos) <= 8 * _ed_scale:
_is_grabbing_min_grab = true
_mouse_click_rel = event.position - _grabber_min_pos
_mouse_click_value = value_min
elif event.position.distance_to(_grabber_max_pos) <= 8 * _ed_scale:
_is_grabbing_max_grab = true
_mouse_click_rel = event.position - _grabber_max_pos
_mouse_click_value = value_max
elif event.position.x < size.x / 2:
_is_grabbing_min_value = true
_mouse_click_value = value_min
else:
_is_grabbing_max_value = true
_mouse_click_value = value_max
else: # Mouse left released
if _mouse_click_pos.distance_to(event.position) <= 8 * _ed_scale:
_grab_end()
if event.position.x < size.x / 2:
_line_edit.text = str(value_min)
_is_editing_min = true
_is_editing_max = false
else:
_line_edit.text = str(value_max)
_is_editing_min = false
_is_editing_max = true
_line_edit.visible = true
_line_edit.select()
_line_edit.grab_focus()
elif _is_grabbing_max_grab or _is_grabbing_max_value:
_grab_end()
queue_redraw()
elif _is_grabbing_min_grab or _is_grabbing_min_value:
_grab_end()
queue_redraw()
elif event.button_index == MOUSE_BUTTON_RIGHT:
if event.pressed:
if _is_grabbing_max_grab or _is_grabbing_max_value:
_grab_end()
set_value_max(_mouse_click_value)
elif _is_grabbing_min_grab or _is_grabbing_min_value:
_grab_end()
set_value_min(_mouse_click_value)
elif event.button_index == MOUSE_BUTTON_WHEEL_UP or event.button_index == MOUSE_BUTTON_WHEEL_LEFT:
if event.pressed:
if event.position.x < size.x / 2:
set_value_min(value_min + step)
else:
set_value_max(value_max + step)
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN or event.button_index == MOUSE_BUTTON_WHEEL_RIGHT:
if event.pressed:
if event.position.x < size.x / 2:
set_value_min(value_min - step)
else:
set_value_max(value_max - step)
func _draw() -> void:
var sb := get_theme_stylebox(&"normal", &"LineEdit")
var font := get_theme_font(&"font", &"LineEdit")
var font_size := get_theme_font_size(&"font_size", &"LineEdit")
var label_min := "%0.0f" % value_min
var label_max := "%0.0f" % value_max
var label_w_min := font.get_string_size(label_min, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
var label_w_max := font.get_string_size(label_max, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size).x
var label_y := (size.y - font.get_height(font_size)) / 2.0 + font.get_ascent(font_size)
var color := get_theme_color(&"font_color", &"LineEdit")
draw_style_box(sb, Rect2(Vector2.ZERO, size))
if has_focus():
var sb_focus = get_theme_stylebox(&"focus", &"LineEdit")
draw_style_box(sb_focus, Rect2(Vector2(), size))
color.a = 0.9
draw_line(Vector2(size.x / 2.0, label_y - 10.0 * _ed_scale), Vector2(size.x / 2.0, label_y), color, max(1.0, _ed_scale))
draw_string(font, Vector2(size.x * 0.25 - label_w_min / 2.0, label_y), label_min, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color)
draw_string(font, Vector2(size.x * 0.75 - label_w_max / 2.0, label_y), label_max, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, color)
_slider_area = Rect2(_ed_scale * 6.0, size.y - _ed_scale * 5.0, size.x - _ed_scale * 12.0, _ed_scale * 2.0)
color.a = 0.2
draw_rect(_slider_area, color)
_slider_range = _slider_area
_slider_range.size.x *= (value_max - value_min) / (range_max - range_min)
_slider_range.position.x += value_min / (range_max - range_min) * _slider_area.size.x
color.a = 0.45
draw_rect(_slider_range, color)
_grabber_min_pos = _slider_range.position
_grabber_max_pos = _slider_range.position + Vector2(_slider_range.size.x, 0)
color.a = 0.9
var tex: Texture2D = null
if not _is_grabbing_max_grab:
if _mouse_over_grabber_min or _is_grabbing_min_grab:
tex = get_theme_icon(&"grabber_highlight", &"HSlider")
elif _mouse_over and not _mouse_over_grabber_max:
tex = get_theme_icon(&"grabber", &"HSlider")
if tex:
draw_texture(tex, (_grabber_min_pos - tex.get_size() / 2.0) + Vector2.DOWN)
else:
draw_rect(Rect2(_grabber_min_pos.x - 2 * _ed_scale, _grabber_min_pos.y - 1 * _ed_scale, 4 * _ed_scale, 4 * _ed_scale), color)
tex = null
if not _is_grabbing_min_grab:
if _mouse_over_grabber_max or _is_grabbing_max_grab:
tex = get_theme_icon(&"grabber_highlight", &"HSlider")
elif _mouse_over and not _mouse_over_grabber_min:
tex = get_theme_icon(&"grabber", &"HSlider")
if tex:
draw_texture(tex, (_grabber_max_pos - tex.get_size() / 2.0) + Vector2.DOWN)
else:
draw_rect(Rect2(_grabber_max_pos.x - 2 * _ed_scale, _grabber_max_pos.y - 1 * _ed_scale, 4 * _ed_scale, 4 * _ed_scale), color)
func set_value(v_min: float, v_max: float) -> void:
v_min = max(min(v_min, v_max), range_min)
v_max = min(max(v_min, v_max), range_max)
if v_min != value_min or v_max != value_max:
value_min = v_min
value_max = v_max
value_changed.emit(value_min, value_max)
_update_tooltip()
queue_redraw()
func set_value_min(value: float) -> void:
value = clampf(snappedf(value, step), range_min, value_max)
if value != value_min:
value_min = value
value_changed.emit(value_min, value_max)
_update_tooltip()
queue_redraw()
func set_value_max(value: float) -> void:
value = clampf(snappedf(value, step), value_min, range_max)
if value != value_max:
value_max = value
value_changed.emit(value_min, value_max)
_update_tooltip()
queue_redraw()
func _update_tooltip() -> void:
tooltip_text = "Slope to avoid:\n"
tooltip_text += "Min: %0.*f°\n" %[step_decimals(step), value_min]
tooltip_text += "Max: %0.*f°\n" %[step_decimals(step), value_max]
func _grab_end() -> void:
Input.mouse_mode = Input.MOUSE_MODE_VISIBLE
if _is_grabbing_max_value:
Input.warp_mouse(_grabber_max_pos + global_position)
elif _is_grabbing_min_value:
Input.warp_mouse(_grabber_min_pos + global_position)
_is_grabbing_min_grab = false
_is_grabbing_max_grab = false
_is_grabbing_min_value = false
_is_grabbing_max_value = false
func _on_theme_changed() -> void:
var es: int = EditorInterface.get_editor_settings().get_setting("interface/editor/display_scale")
if es == 7:
_ed_scale = EditorInterface.get_editor_settings().get_setting("interface/editor/custom_display_scale")
else:
_ed_scale = [1.0, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0][clamp(es, 0, 6)]
_line_edit.caret_blink = EditorInterface.get_editor_settings().get_setting("text_editor/appearance/caret/caret_blink")
func _on_line_edit_text_submitted(new_text: String) -> void:
var value := range_min
var expression = Expression.new()
if expression.parse(new_text) == OK:
var result = expression.execute()
if not expression.has_execute_failed() and (typeof(result) == TYPE_FLOAT or typeof(result) == TYPE_INT):
value = result
if _is_editing_min:
set_value_min(value)
elif _is_editing_max:
set_value_max(value)
_is_editing_min = false
_is_editing_max = false
_line_edit.visible = false
grab_focus()
func _on_line_edit_focus_exited() -> void:
_line_edit.visible = false
func _on_line_edit_gui_input(event: InputEvent) -> void:
if event is InputEventKey:
if event.keycode == KEY_ESCAPE and event.pressed:
_line_edit.visible = false
grab_focus()

View File

@ -0,0 +1 @@
uid://dmpm4vrmag0ru

View File

@ -0,0 +1,28 @@
[gd_scene load_steps=2 format=3 uid="uid://r30ih7dtx1uo"]
[ext_resource type="Script" uid="uid://dmpm4vrmag0ru" path="res://addons/simplegrasstextured/gui/domain_range.gd" id="1_xiopc"]
[node name="DomainRange" type="Control"]
custom_minimum_size = Vector2(80, 0)
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
focus_mode = 2
script = ExtResource("1_xiopc")
[node name="LineEdit" type="LineEdit" parent="."]
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
caret_blink = true
[connection signal="focus_exited" from="LineEdit" to="." method="_on_line_edit_focus_exited"]
[connection signal="gui_input" from="LineEdit" to="." method="_on_line_edit_gui_input"]
[connection signal="text_submitted" from="LineEdit" to="." method="_on_line_edit_text_submitted"]

View File

@ -0,0 +1,212 @@
# global_parameters.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends AcceptDialog
const DEFAULT_WIND_DIR := Vector3.RIGHT
const DEFAULT_WIND_STRENGTH := 0.15
const DEFAULT_WIND_TURBULENCE := 1.0
const DEFAULT_WIND_PATTERN := "res://addons/simplegrasstextured/images/wind_pattern.png"
var _wind_dir_x_slider :EditorSpinSlider
var _wind_dir_y_slider :EditorSpinSlider
var _wind_dir_z_slider :EditorSpinSlider
var _wind_strength_slider :EditorSpinSlider
var _wind_turbulence_slider :EditorSpinSlider
var _wind_pattern :EditorResourcePicker
func _ready() -> void:
name = &"SimpleGrassTexturedGlobalParameters"
size = Vector2.ZERO
_wind_dir_x_slider = _create_slider("X", -1, 1, 0.01)
_wind_dir_y_slider = _create_slider("Y", -1, 1, 0.01)
_wind_dir_z_slider = _create_slider("Z", -1, 1, 0.01)
_wind_strength_slider = _create_slider("", 0, 1, 0.001)
_wind_turbulence_slider = _create_slider("", 0, 20, 0.01)
_wind_pattern = EditorResourcePicker.new()
_wind_pattern.base_type = "Texture"
_wind_pattern.size_flags_horizontal = Control.SIZE_EXPAND_FILL
_wind_dir_x_slider.value_changed.connect(_on_wind_dir_x_value_changed)
_wind_dir_y_slider.value_changed.connect(_on_wind_dir_y_value_changed)
_wind_dir_z_slider.value_changed.connect(_on_wind_dir_z_value_changed)
_wind_strength_slider.value_changed.connect(_on_wind_strength_value_changed)
_wind_turbulence_slider.value_changed.connect(_on_wind_turbulence_value_changed)
_wind_pattern.resource_changed.connect(_on_wind_pattern_resource_changed)
%WindDirHbox.add_child(_wind_dir_x_slider)
%WindDirHbox.add_child(_wind_dir_y_slider)
%WindDirHbox.add_child(_wind_dir_z_slider)
%WindStrengthHBox.add_child(_wind_strength_slider)
%WindTurbulenceHBox.add_child(_wind_turbulence_slider)
%WindPatternHBox.add_child(_wind_pattern)
get_ok_button().custom_minimum_size.x = 100
func _create_slider(label :String, min :float, max :float, step :float) -> EditorSpinSlider:
var slider := EditorSpinSlider.new()
slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
slider.step = step;
slider.min_value = min
slider.max_value = max
slider.label = label
slider.custom_minimum_size.x = 80
return slider
func disable_button(button :Button, disabled :bool) -> void:
if disabled:
button.disabled = true
button.modulate.a = 0
button.focus_mode = Control.FOCUS_NONE
else:
button.disabled = false
button.modulate.a = 1
button.focus_mode = Control.FOCUS_ALL
func on_theme_changed() -> void:
%DefaultWindDirButton.icon = get_theme_icon(&"Reload", &"EditorIcons")
%DefaultWindStrengthButton.icon = get_theme_icon(&"Reload", &"EditorIcons")
%DefaultWindTurbulenceButton.icon = get_theme_icon(&"Reload", &"EditorIcons")
%DefaultWindPatternButton.icon = get_theme_icon(&"Reload", &"EditorIcons")
func _on_about_to_popup() -> void:
var windir :Vector3 = ProjectSettings.get_setting("shader_globals/sgt_wind_direction").value
_wind_dir_x_slider.value = windir.x
_wind_dir_y_slider.value = windir.y
_wind_dir_z_slider.value = windir.z
_wind_strength_slider.value = ProjectSettings.get_setting("shader_globals/sgt_wind_strength").value
_wind_turbulence_slider.value = ProjectSettings.get_setting("shader_globals/sgt_wind_turbulence").value
_wind_pattern.edited_resource = load(ProjectSettings.get_setting("shader_globals/sgt_wind_pattern").value)
_on_wind_pattern_resource_changed(_wind_pattern.edited_resource)
on_theme_changed()
func _on_wind_dir_x_value_changed(value :float) -> void:
var windir :Vector3 = ProjectSettings.get_setting("shader_globals/sgt_wind_direction", {"value":Vector3.RIGHT}).value
windir.x = value
ProjectSettings.set_setting("shader_globals/sgt_wind_direction", {
"type": "vec3",
"value": windir
})
RenderingServer.global_shader_parameter_set("sgt_wind_direction", windir)
$SaveConfigTimer.start()
get_tree().emit_signal(&"sgt_globals_params_changed")
disable_button(%DefaultWindDirButton, windir == DEFAULT_WIND_DIR)
func _on_wind_dir_y_value_changed(value :float) -> void:
var windir :Vector3 = ProjectSettings.get_setting("shader_globals/sgt_wind_direction", {"value":Vector3.RIGHT}).value
windir.y = value
ProjectSettings.set_setting("shader_globals/sgt_wind_direction", {
"type": "vec3",
"value": windir
})
RenderingServer.global_shader_parameter_set("sgt_wind_direction", windir)
$SaveConfigTimer.start()
get_tree().emit_signal(&"sgt_globals_params_changed")
disable_button(%DefaultWindDirButton, windir == DEFAULT_WIND_DIR)
func _on_wind_dir_z_value_changed(value :float) -> void:
var windir :Vector3 = ProjectSettings.get_setting("shader_globals/sgt_wind_direction", {"value":Vector3.RIGHT}).value
windir.z = value
ProjectSettings.set_setting("shader_globals/sgt_wind_direction", {
"type": "vec3",
"value": windir
})
RenderingServer.global_shader_parameter_set("sgt_wind_direction", windir)
$SaveConfigTimer.start()
get_tree().emit_signal(&"sgt_globals_params_changed")
disable_button(%DefaultWindDirButton, windir == DEFAULT_WIND_DIR)
func _on_wind_strength_value_changed(value :float) -> void:
ProjectSettings.set_setting("shader_globals/sgt_wind_strength", {
"type": "float",
"value": value
})
RenderingServer.global_shader_parameter_set("sgt_wind_strength", value)
$SaveConfigTimer.start()
get_tree().emit_signal(&"sgt_globals_params_changed")
disable_button(%DefaultWindStrengthButton, _wind_strength_slider.value == DEFAULT_WIND_STRENGTH)
func _on_wind_turbulence_value_changed(value :float) -> void:
ProjectSettings.set_setting("shader_globals/sgt_wind_turbulence", {
"type": "float",
"value": value
})
RenderingServer.global_shader_parameter_set("sgt_wind_turbulence", value)
$SaveConfigTimer.start()
get_tree().emit_signal(&"sgt_globals_params_changed")
disable_button(%DefaultWindTurbulenceButton, _wind_turbulence_slider.value == DEFAULT_WIND_TURBULENCE)
func _on_wind_pattern_resource_changed(resource :Resource) -> void:
if resource.resource_path == "":
_wind_pattern.edited_resource = load(DEFAULT_WIND_PATTERN)
_on_wind_pattern_resource_changed(_wind_pattern.edited_resource)
return
ProjectSettings.set_setting("shader_globals/sgt_wind_pattern", {
"type": "sampler2D",
"value": resource.resource_path
})
RenderingServer.global_shader_parameter_set("sgt_wind_pattern", load(resource.resource_path))
$SaveConfigTimer.start()
get_tree().emit_signal(&"sgt_globals_params_changed")
disable_button(%DefaultWindPatternButton, resource.resource_path == DEFAULT_WIND_PATTERN)
func _on_save_config_timer_timeout() -> void:
ProjectSettings.save()
func _on_canceled():
queue_free()
func _on_confirmed() -> void:
ProjectSettings.save()
queue_free()
func _on_default_wind_dir_button_pressed() -> void:
_wind_dir_x_slider.value = DEFAULT_WIND_DIR.x
_wind_dir_y_slider.value = DEFAULT_WIND_DIR.y
_wind_dir_z_slider.value = DEFAULT_WIND_DIR.z
func _on_default_wind_strength_button_pressed() -> void:
_wind_strength_slider.value = DEFAULT_WIND_STRENGTH
func _on_default_wind_turbulence_button_pressed() -> void:
_wind_turbulence_slider.value = DEFAULT_WIND_TURBULENCE
func _on_default_wind_pattern_button_pressed() -> void:
_wind_pattern.edited_resource = load(DEFAULT_WIND_PATTERN)
_on_wind_pattern_resource_changed(_wind_pattern.edited_resource)

View File

@ -0,0 +1 @@
uid://b1ddeyowx86m3

View File

@ -0,0 +1,90 @@
[gd_scene load_steps=2 format=3 uid="uid://bbbcb5o176677"]
[ext_resource type="Script" uid="uid://b1ddeyowx86m3" path="res://addons/simplegrasstextured/gui/global_parameters.gd" id="1_f1c1a"]
[node name="SimpleGrassTexturedGlobalParameters" type="AcceptDialog"]
title = "Simple Grass Texture: Global Parameters"
initial_position = 2
size = Vector2i(197, 181)
unresizable = true
script = ExtResource("1_f1c1a")
[node name="MarginContainer" type="MarginContainer" parent="."]
offset_left = 8.0
offset_top = 8.0
offset_right = 441.0
offset_bottom = 164.0
theme_override_constants/margin_left = 10
theme_override_constants/margin_top = 10
theme_override_constants/margin_right = 10
theme_override_constants/margin_bottom = 10
[node name="GridContainer" type="GridContainer" parent="MarginContainer"]
layout_mode = 2
theme_override_constants/h_separation = 20
columns = 2
[node name="Label" type="Label" parent="MarginContainer/GridContainer"]
layout_mode = 2
text = "Wind direction:"
[node name="WindDirHbox" type="HBoxContainer" parent="MarginContainer/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="DefaultWindDirButton" type="Button" parent="MarginContainer/GridContainer/WindDirHbox"]
unique_name_in_owner = true
layout_mode = 2
flat = true
[node name="Label2" type="Label" parent="MarginContainer/GridContainer"]
layout_mode = 2
text = "Wind strength:"
[node name="WindStrengthHBox" type="HBoxContainer" parent="MarginContainer/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="DefaultWindStrengthButton" type="Button" parent="MarginContainer/GridContainer/WindStrengthHBox"]
unique_name_in_owner = true
layout_mode = 2
flat = true
[node name="Label3" type="Label" parent="MarginContainer/GridContainer"]
layout_mode = 2
text = "Wind turbulence:"
[node name="WindTurbulenceHBox" type="HBoxContainer" parent="MarginContainer/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="DefaultWindTurbulenceButton" type="Button" parent="MarginContainer/GridContainer/WindTurbulenceHBox"]
unique_name_in_owner = true
layout_mode = 2
flat = true
[node name="Label4" type="Label" parent="MarginContainer/GridContainer"]
layout_mode = 2
text = "Wind pattern:"
[node name="WindPatternHBox" type="HBoxContainer" parent="MarginContainer/GridContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="DefaultWindPatternButton" type="Button" parent="MarginContainer/GridContainer/WindPatternHBox"]
unique_name_in_owner = true
layout_mode = 2
flat = true
[node name="SaveConfigTimer" type="Timer" parent="."]
wait_time = 5.0
one_shot = true
[connection signal="about_to_popup" from="." to="." method="_on_about_to_popup"]
[connection signal="canceled" from="." to="." method="_on_canceled"]
[connection signal="confirmed" from="." to="." method="_on_confirmed"]
[connection signal="pressed" from="MarginContainer/GridContainer/WindDirHbox/DefaultWindDirButton" to="." method="_on_default_wind_dir_button_pressed"]
[connection signal="pressed" from="MarginContainer/GridContainer/WindStrengthHBox/DefaultWindStrengthButton" to="." method="_on_default_wind_strength_button_pressed"]
[connection signal="pressed" from="MarginContainer/GridContainer/WindTurbulenceHBox/DefaultWindTurbulenceButton" to="." method="_on_default_wind_turbulence_button_pressed"]
[connection signal="pressed" from="MarginContainer/GridContainer/WindPatternHBox/DefaultWindPatternButton" to="." method="_on_default_wind_pattern_button_pressed"]
[connection signal="timeout" from="SaveConfigTimer" to="." method="_on_save_config_timer_timeout"]

View File

@ -0,0 +1,337 @@
# toolbar.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends Control
enum MENU_SHAPE_ID {
TOOL_SHAPE_SPHERE,
TOOL_SHAPE_CYLINDER,
TOOL_SHAPE_CYLINDER_INF_H,
TOOL_SHAPE_BOX,
TOOL_SHAPE_BOX_INF_H,
}
const DEFAULT_RADIUS := 2.0
const DEFAULT_DENSITY := 25.0
const DEFAULT_SCALE := 1.0
const DEFAULT_ROTATION := 0.0
const DEFAULT_ROTATION_RAND := 1.0
const DEFAULT_DISTANCE := 0.25
var _shortcut_radius_inc := Shortcut.new()
var _shortcut_radius_dec := Shortcut.new()
var _shortcut_density_inc := Shortcut.new()
var _shortcut_density_dec := Shortcut.new()
var _plugin: EditorPlugin = null
var _grass_selected = null
@onready var button_airbrush :Button = %ButtonAirbrush
@onready var button_pencil :Button = %ButtonPencil
@onready var button_eraser :Button = %ButtonEraser
@onready var button_density :Button = %IconDensity
@onready var slider_radius :HSlider = $HSliderRadius
@onready var slider_density :HSlider = $HSliderDensity
@onready var edit_slope_range: Control = %SlopeRange
@onready var edit_scale :EditorSpinSlider
@onready var edit_rotation :EditorSpinSlider
@onready var edit_rotation_rand :EditorSpinSlider
@onready var edit_distance :EditorSpinSlider
@onready var label_stats :Label = $LabelStats
@onready var _label_radius :Label = $HSliderRadius/Label
@onready var _label_density :Label = $HSliderDensity/Label
@onready var _button_more :MenuButton = $ButtonMore
@onready var _airbrush_options: MenuButton = %AirbrushOptions
@onready var _pencil_options: MenuButton = %PencilOptions
@onready var _eraser_options: MenuButton = %EraserOptions
@onready var _tween_radius :Tween = null
@onready var _tween_density :Tween = null
func _ready() -> void:
edit_scale = _create_slider("", 0.01, 10.0, 0.01, DEFAULT_SCALE)
edit_rotation = _create_slider("", 0.0, 360.0, 0.1, DEFAULT_ROTATION)
edit_rotation_rand = _create_slider("", 0.0, 1.0, 0.01, DEFAULT_ROTATION_RAND)
edit_distance = _create_slider("", 0.0, 5.0, 0.01, DEFAULT_DISTANCE)
%ScaleCont.add_child(edit_scale)
%RotationCont.add_child(edit_rotation)
%RotationRandCont.add_child(edit_rotation_rand)
%DistanceCont.add_child(edit_distance)
func _unhandled_input(event :InputEvent) -> void:
if not event.is_pressed():
return
if _shortcut_radius_inc.matches_event(event):
slider_radius.value += 0.1
if _shortcut_radius_dec.matches_event(event):
slider_radius.value -= 0.1
if slider_density.editable and _shortcut_density_inc.matches_event(event):
slider_density.value += 1
if slider_density.editable and _shortcut_density_dec.matches_event(event):
slider_density.value -= 1
func set_plugin(plugin :EditorPlugin) -> void:
_plugin = plugin
theme_changed.connect(_on_theme_changed)
%ButtonMore.set_plugin(plugin)
var config := ConfigFile.new()
config.load("res://addons/simplegrasstextured/plugin.cfg")
%LabelVersion.text = config.get_value("plugin", "version")
button_airbrush.shortcut = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/airbrush_tool")
button_pencil.shortcut = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/pencil_tool")
button_eraser.shortcut = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/eraser_tool")
button_airbrush.gui_input.connect(_on_button_tool_gui_input.bind(button_airbrush, _airbrush_options))
button_pencil.gui_input.connect(_on_button_tool_gui_input.bind(button_pencil, _pencil_options))
button_eraser.gui_input.connect(_on_button_tool_gui_input.bind(button_eraser, _eraser_options))
_shortcut_radius_inc = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/radius_increment")
_shortcut_radius_dec = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/radius_decrement")
_shortcut_density_inc = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/density_increment")
_shortcut_density_dec = plugin.get_custom_setting("SimpleGrassTextured/Shortcuts/density_decrement")
_airbrush_options.get_popup().add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_airbrush_options.get_popup().add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_airbrush_options.get_popup().about_to_popup.connect(_on_tool_options_about_to_popup.bind(_airbrush_options))
_airbrush_options.get_popup().id_pressed.connect(_on_sgt_shape_menu_pressed.bind("airbrush", _airbrush_options.get_popup()))
_pencil_options.get_popup().add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_pencil_options.get_popup().add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_pencil_options.get_popup().about_to_popup.connect(_on_tool_options_about_to_popup.bind(_pencil_options))
_pencil_options.get_popup().id_pressed.connect(_on_sgt_shape_menu_pressed.bind("pencil", _pencil_options.get_popup()))
_eraser_options.get_popup().add_radio_check_item("Sphere", MENU_SHAPE_ID.TOOL_SHAPE_SPHERE)
_eraser_options.get_popup().add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_eraser_options.get_popup().add_radio_check_item("Infinite vertical cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H)
_eraser_options.get_popup().add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_eraser_options.get_popup().add_radio_check_item("Infinite vertical box", MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H)
_eraser_options.get_popup().about_to_popup.connect(_on_tool_options_about_to_popup.bind(_eraser_options))
_eraser_options.get_popup().id_pressed.connect(_on_sgt_shape_menu_pressed.bind("eraser", _eraser_options.get_popup()))
_on_theme_changed()
func set_current_grass(grass) -> void:
_grass_selected = grass
%ButtonMore.set_current_grass(grass)
if _grass_selected == null:
return
for tool_name in _grass_selected.sgt_tool_shape:
match tool_name:
"airbrush":
_update_shape_menu_from_grass(_airbrush_options.get_popup(), _grass_selected.sgt_tool_shape[tool_name])
"pencil":
_update_shape_menu_from_grass(_pencil_options.get_popup(), _grass_selected.sgt_tool_shape[tool_name])
"eraser":
_update_shape_menu_from_grass(_eraser_options.get_popup(), _grass_selected.sgt_tool_shape[tool_name])
func set_density_modulate(color: Color) -> void:
%IconDensity.self_modulate = color
slider_density.modulate = color
func _create_slider(label :String, min_value :float, max_value :float, step :float, value :float = 0.0) -> EditorSpinSlider:
var slider := EditorSpinSlider.new()
slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
slider.step = step;
slider.min_value = min_value
slider.max_value = max_value
slider.label = label
slider.value = value
slider.custom_minimum_size.x = 75
return slider
func _on_tool_options_about_to_popup(_button_options: MenuButton) -> void:
set_current_grass(_grass_selected)
func _update_shape_menu_from_grass(popupmenu: PopupMenu, plugin_id_shape: int) -> void:
for i in popupmenu.item_count:
popupmenu.set_item_checked(i, false)
var idx := -1
match _plugin.get_tool_shape_name(plugin_id_shape):
"sphere":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_SPHERE)
"cylinder":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
"cylinder_inf_h":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H)
"box":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_BOX)
"box_inf_h":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H)
_:
idx = -1
if idx == -1:
return
popupmenu.set_item_checked(idx, true)
func _on_sgt_shape_menu_pressed(id: int, tool_name: String, popupmenu: PopupMenu) -> void:
if _grass_selected == null:
return
for i in popupmenu.item_count:
popupmenu.set_item_checked(i, false)
var idx := popupmenu.get_item_index(id)
if idx == -1:
return
popupmenu.set_item_checked(idx, true)
var shape_name := ""
match id:
MENU_SHAPE_ID.TOOL_SHAPE_SPHERE:
shape_name = "sphere"
MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER:
shape_name = "cylinder"
MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H:
shape_name = "cylinder_inf_h"
MENU_SHAPE_ID.TOOL_SHAPE_BOX:
shape_name = "box"
MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H:
shape_name = "box_inf_h"
_plugin.set_tool_shape(tool_name, shape_name)
_plugin.set_tool(tool_name)
func _on_h_slider_radius_value_changed(value :float) -> void:
if _tween_radius != null:
_tween_radius.kill()
_label_radius.set(&"theme_override_colors/font_outline_color", _label_radius.get_theme_color(&"font_color").inverted())
_tween_radius = _label_radius.create_tween()
_tween_radius.tween_property(_label_radius, ^"modulate", Color.WHITE, 0.1)
_tween_radius.tween_interval(2)
_tween_radius.tween_property(_label_radius, ^"modulate", Color(1,1,1,0), 2)
_label_radius.text = "%0.1f" % value
slider_radius.tooltip_text = "(" + _shortcut_radius_dec.get_as_text() + ") - (" + _shortcut_radius_inc.get_as_text() + ")\n"
slider_radius.tooltip_text += "Radius = %0.1f" % value
func _on_h_slider_density_value_changed(value :float) -> void:
if _tween_density != null:
_tween_density.kill()
_label_density.set(&"theme_override_colors/font_outline_color", _label_density.get_theme_color(&"font_color").inverted())
_tween_density = _label_density.create_tween()
_tween_density.tween_property(_label_density, ^"modulate", Color.WHITE, 0.1)
_tween_density.tween_interval(2)
_tween_density.tween_property(_label_density, ^"modulate", Color(1,1,1,0), 2)
_label_density.text = str(value)
slider_density.tooltip_text = "(" + _shortcut_density_dec.get_as_text() + ") - (" + _shortcut_density_inc.get_as_text() + ")\n"
slider_density.tooltip_text += "Density = %0.0f" % value
func _on_theme_changed() -> void:
%IconScale.icon = get_theme_icon(&"ToolScale", &"EditorIcons")
%IconRotation.icon = get_theme_icon(&"ToolRotate", &"EditorIcons")
%IconRotationRand.icon = get_theme_icon(&"RandomNumberGenerator", &"EditorIcons")
var base_color: Color = EditorInterface.get_base_control().get_theme_color(&"base_color", &"Editor")
if base_color.get_luminance() < 0.5:
button_airbrush.icon = load("res://addons/simplegrasstextured/images/sgt_icon_airbrush.svg")
button_pencil.icon = load("res://addons/simplegrasstextured/images/sgt_icon_pen.svg")
button_eraser.icon = load("res://addons/simplegrasstextured/images/sgt_icon_eraser.svg")
_airbrush_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up.svg")
_pencil_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up.svg")
_eraser_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up.svg")
%IconSlope.icon = load("res://addons/simplegrasstextured/images/sgt_icon_slope.svg")
%IconRadius.icon = load("res://addons/simplegrasstextured/images/sgt_icon_radius.svg")
%IconDensity.icon = load("res://addons/simplegrasstextured/images/sgt_icon_density.svg")
%IconDistance.icon = load("res://addons/simplegrasstextured/images/sgt_icon_distance.svg")
else:
button_airbrush.icon = load("res://addons/simplegrasstextured/images/sgt_icon_airbrush_dark.svg")
button_pencil.icon = load("res://addons/simplegrasstextured/images/sgt_icon_pen_dark.svg")
button_eraser.icon = load("res://addons/simplegrasstextured/images/sgt_icon_eraser_dark.svg")
_airbrush_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up_dark.svg")
_pencil_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up_dark.svg")
_eraser_options.icon = load("res://addons/simplegrasstextured/images/sgt_icon_arrow_up_dark.svg")
%IconSlope.icon = load("res://addons/simplegrasstextured/images/sgt_icon_slope_dark.svg")
%IconRadius.icon = load("res://addons/simplegrasstextured/images/sgt_icon_radius_dark.svg")
%IconDensity.icon = load("res://addons/simplegrasstextured/images/sgt_icon_density_dark.svg")
%IconDistance.icon = load("res://addons/simplegrasstextured/images/sgt_icon_distance_dark.svg")
if _button_more != null:
_button_more.icon = get_theme_icon(&"GuiTabMenuHl", &"EditorIcons")
# Test that the icons size matches with editor UI scale
var _ed_scale := 1.0
var es: int = EditorInterface.get_editor_settings().get_setting("interface/editor/display_scale")
if es == 7:
_ed_scale = EditorInterface.get_editor_settings().get_setting("interface/editor/custom_display_scale")
else:
_ed_scale = [1.0, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0][clamp(es, 0, 6)]
if load("res://addons/simplegrasstextured/images/sgt_icon_density.svg").get_width() != roundi(16 * _ed_scale):
$TimerReimportIcons.start()
func _on_panel_container_gui_input(event) -> void:
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
var win = load("res://addons/simplegrasstextured/gui/about.tscn").instantiate()
get_window().add_child(win)
win.popup_centered()
func _on_icon_slope_pressed() -> void:
edit_slope_range.set_value_min(0)
edit_slope_range.set_value_max(45)
func _on_icon_scale_pressed() -> void:
edit_scale.value = DEFAULT_SCALE
func _on_icon_rotation_pressed() -> void:
edit_rotation.value = DEFAULT_ROTATION
func _on_icon_rotation_rand_pressed() -> void:
edit_rotation_rand.value = DEFAULT_ROTATION_RAND
func _on_icon_distance_pressed() -> void:
edit_distance.value = DEFAULT_DISTANCE
func _on_icon_radius_pressed() -> void:
slider_radius.value = DEFAULT_RADIUS
func _on_icon_radius_2_pressed() -> void:
slider_density.value = DEFAULT_DENSITY
func _on_timer_reimport_icons_timeout() -> void:
if EditorInterface.get_resource_filesystem().is_scanning():
return
$TimerReimportIcons.stop()
EditorInterface.get_resource_filesystem().reimport_files([
"res://addons/simplegrasstextured/sgt_icon.svg",
"res://addons/simplegrasstextured/sgt_icon_48.svg",
"res://addons/simplegrasstextured/images/sgt_icon_density.svg",
"res://addons/simplegrasstextured/images/sgt_icon_distance.svg",
"res://addons/simplegrasstextured/images/sgt_icon_radius.svg",
"res://addons/simplegrasstextured/images/sgt_icon_slope.svg",
"res://addons/simplegrasstextured/images/sgt_icon_airbrush.svg",
"res://addons/simplegrasstextured/images/sgt_icon_pen.svg",
"res://addons/simplegrasstextured/images/sgt_icon_eraser.svg"
])
func _on_button_tool_gui_input(event: InputEvent, tool: Button, button_options: MenuButton) -> void:
if event is InputEventMouseButton:
if event.pressed and event.button_index == MOUSE_BUTTON_RIGHT:
button_options.get_popup().popup(Rect2(tool.global_position + Vector2(tool.size.x, 0), Vector2.ZERO))

View File

@ -0,0 +1 @@
uid://usxgr64t746m

View File

@ -0,0 +1,305 @@
[gd_scene load_steps=16 format=3 uid="uid://dsim5cbhc6kwm"]
[ext_resource type="Script" uid="uid://usxgr64t746m" path="res://addons/simplegrasstextured/gui/toolbar.gd" id="1_aa1o6"]
[ext_resource type="Material" uid="uid://cl74usvfymgvs" path="res://addons/simplegrasstextured/materials/icon.material" id="2_p7k4b"]
[ext_resource type="Texture2D" uid="uid://bsmcpupaj02v8" path="res://addons/simplegrasstextured/sgt_icon.svg" id="3_tl64l"]
[ext_resource type="PackedScene" uid="uid://der2mmxtog2mt" path="res://addons/simplegrasstextured/gui/toolbar_menu.tscn" id="4_1u4kn"]
[ext_resource type="Texture2D" uid="uid://c2kunxv5wepih" path="res://addons/simplegrasstextured/images/sgt_icon_airbrush.svg" id="4_weijg"]
[ext_resource type="Texture2D" uid="uid://clm6vd2es8xm7" path="res://addons/simplegrasstextured/images/sgt_icon_radius.svg" id="5_55kgh"]
[ext_resource type="Texture2D" uid="uid://dtal42g10x1ay" path="res://addons/simplegrasstextured/images/sgt_icon_pen.svg" id="5_itpep"]
[ext_resource type="Texture2D" uid="uid://bda52g6s88x5d" path="res://addons/simplegrasstextured/images/sgt_icon_arrow_up.svg" id="5_jg7h4"]
[ext_resource type="Texture2D" uid="uid://brugf6rnck1tk" path="res://addons/simplegrasstextured/images/sgt_icon_eraser.svg" id="6_jg7h4"]
[ext_resource type="Texture2D" uid="uid://tqiak77omdnr" path="res://addons/simplegrasstextured/images/sgt_icon_density.svg" id="6_wboky"]
[ext_resource type="Texture2D" uid="uid://24avwtyvf01r" path="res://addons/simplegrasstextured/images/sgt_icon_slope.svg" id="7_4wrj3"]
[ext_resource type="Texture2D" uid="uid://d28dlfkm62t6e" path="res://addons/simplegrasstextured/images/sgt_icon_distance.svg" id="7_ao3h0"]
[ext_resource type="PackedScene" uid="uid://r30ih7dtx1uo" path="res://addons/simplegrasstextured/gui/domain_range.tscn" id="8_y7yf0"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6mh15"]
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hc7jq"]
[node name="SGTToolbar" type="HFlowContainer"]
anchors_preset = 10
anchor_right = 1.0
offset_bottom = 31.0
grow_horizontal = 2
script = ExtResource("1_aa1o6")
[node name="PanelContainer" type="PanelContainer" parent="."]
custom_minimum_size = Vector2(24, 0)
layout_mode = 2
mouse_default_cursor_shape = 2
theme_override_styles/panel = SubResource("StyleBoxEmpty_6mh15")
[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer"]
layout_mode = 2
mouse_filter = 2
alignment = 1
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer"]
custom_minimum_size = Vector2(24, 0)
layout_mode = 2
mouse_filter = 2
theme_override_constants/margin_bottom = 3
[node name="TextureRect" type="TextureRect" parent="PanelContainer/VBoxContainer/MarginContainer"]
material = ExtResource("2_p7k4b")
custom_minimum_size = Vector2(24, 0)
layout_mode = 2
mouse_filter = 2
texture = ExtResource("3_tl64l")
stretch_mode = 3
[node name="LabelVersion" type="Label" parent="PanelContainer"]
unique_name_in_owner = true
auto_translate_mode = 2
clip_contents = true
layout_mode = 2
size_flags_vertical = 1
localize_numeral_system = false
theme_override_font_sizes/font_size = 7
theme_override_styles/normal = SubResource("StyleBoxEmpty_hc7jq")
text = "2.0.5"
horizontal_alignment = 1
vertical_alignment = 2
[node name="VSeparator" type="VSeparator" parent="."]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 0
[node name="ButtonAirbrush" type="Button" parent="HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Airdraw tool
(right button for more options)"
theme_type_variation = &"FlatButton"
toggle_mode = true
icon = ExtResource("4_weijg")
[node name="AirbrushOptions" type="MenuButton" parent="HBoxContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_type_variation = &"FlatMenuButton"
icon = ExtResource("5_jg7h4")
flat = false
[node name="HBoxContainer2" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 0
[node name="ButtonPencil" type="Button" parent="HBoxContainer2"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Pencil tool
(right button for more options)"
theme_type_variation = &"FlatButton"
toggle_mode = true
icon = ExtResource("5_itpep")
[node name="PencilOptions" type="MenuButton" parent="HBoxContainer2"]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_type_variation = &"FlatMenuButton"
icon = ExtResource("5_jg7h4")
flat = false
[node name="HBoxContainer3" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 0
[node name="ButtonEraser" type="Button" parent="HBoxContainer3"]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Eraser tool
(right button for more options)"
theme_type_variation = &"FlatButton"
toggle_mode = true
icon = ExtResource("6_jg7h4")
[node name="EraserOptions" type="MenuButton" parent="HBoxContainer3"]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_type_variation = &"FlatMenuButton"
icon = ExtResource("5_jg7h4")
flat = false
[node name="ButtonMore" parent="." instance=ExtResource("4_1u4kn")]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"FlatMenuButton"
flat = false
switch_on_hover = true
[node name="VSeparator2" type="VSeparator" parent="."]
layout_mode = 2
[node name="IconRadius" type="Button" parent="."]
unique_name_in_owner = true
texture_filter = 1
layout_mode = 2
tooltip_text = "Radius"
icon = ExtResource("5_55kgh")
flat = true
[node name="HSliderRadius" type="HSlider" parent="."]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
size_flags_vertical = 1
tooltip_text = "Radius"
min_value = 0.1
max_value = 10.0
step = 0.1
value = 1.0
[node name="Label" type="Label" parent="HSliderRadius"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 2
horizontal_alignment = 1
vertical_alignment = 1
[node name="Separator" type="Control" parent="."]
custom_minimum_size = Vector2(15, 0)
layout_mode = 2
[node name="IconDensity" type="Button" parent="."]
unique_name_in_owner = true
texture_filter = 1
layout_mode = 2
tooltip_text = "Density"
icon = ExtResource("6_wboky")
flat = true
[node name="HSliderDensity" type="HSlider" parent="."]
custom_minimum_size = Vector2(100, 0)
layout_mode = 2
size_flags_vertical = 1
tooltip_text = "Density"
min_value = 1.0
max_value = 200.0
value = 1.0
[node name="Label" type="Label" parent="HSliderDensity"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_colors/font_outline_color = Color(0, 0, 0, 1)
theme_override_constants/outline_size = 2
horizontal_alignment = 1
vertical_alignment = 1
[node name="Separator2" type="Control" parent="."]
custom_minimum_size = Vector2(15, 0)
layout_mode = 2
[node name="SlopeCont" type="HBoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="IconSlope" type="Button" parent="SlopeCont"]
unique_name_in_owner = true
texture_filter = 1
layout_mode = 2
tooltip_text = "Slope"
icon = ExtResource("7_4wrj3")
flat = true
[node name="SlopeRange" parent="SlopeCont" instance=ExtResource("8_y7yf0")]
unique_name_in_owner = true
layout_mode = 2
tooltip_text = "Slope to avoid:
Min: 0°
Max: 45°
"
[node name="ScaleCont" type="HBoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="IconScale" type="Button" parent="ScaleCont"]
unique_name_in_owner = true
texture_filter = 1
layout_mode = 2
tooltip_text = "Scale"
flat = true
[node name="Separator3" type="Control" parent="."]
custom_minimum_size = Vector2(15, 0)
layout_mode = 2
[node name="RotationCont" type="HBoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="IconRotation" type="Button" parent="RotationCont"]
unique_name_in_owner = true
texture_filter = 1
layout_mode = 2
tooltip_text = "Rotation"
flat = true
[node name="RotationRandCont" type="HBoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="IconRotationRand" type="Button" parent="RotationRandCont"]
unique_name_in_owner = true
texture_filter = 1
layout_mode = 2
tooltip_text = "Rotation randomizer"
flat = true
[node name="Separator4" type="Control" parent="."]
custom_minimum_size = Vector2(15, 0)
layout_mode = 2
[node name="DistanceCont" type="HBoxContainer" parent="."]
unique_name_in_owner = true
layout_mode = 2
[node name="IconDistance" type="Button" parent="DistanceCont"]
unique_name_in_owner = true
texture_filter = 1
layout_mode = 2
tooltip_text = "Minimum distance between grasses"
icon = ExtResource("7_ao3h0")
flat = true
[node name="Separator5" type="Control" parent="."]
custom_minimum_size = Vector2(15, 0)
layout_mode = 2
[node name="LabelStats" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
text = "Count: 0"
horizontal_alignment = 2
vertical_alignment = 1
[node name="TimerReimportIcons" type="Timer" parent="."]
[connection signal="gui_input" from="PanelContainer" to="." method="_on_panel_container_gui_input"]
[connection signal="pressed" from="IconRadius" to="." method="_on_icon_radius_pressed"]
[connection signal="value_changed" from="HSliderRadius" to="." method="_on_h_slider_radius_value_changed"]
[connection signal="pressed" from="IconDensity" to="." method="_on_icon_radius_2_pressed"]
[connection signal="value_changed" from="HSliderDensity" to="." method="_on_h_slider_density_value_changed"]
[connection signal="pressed" from="SlopeCont/IconSlope" to="." method="_on_icon_slope_pressed"]
[connection signal="pressed" from="ScaleCont/IconScale" to="." method="_on_icon_scale_pressed"]
[connection signal="pressed" from="RotationCont/IconRotation" to="." method="_on_icon_rotation_pressed"]
[connection signal="pressed" from="RotationRandCont/IconRotationRand" to="." method="_on_icon_rotation_rand_pressed"]
[connection signal="pressed" from="DistanceCont/IconDistance" to="." method="_on_icon_distance_pressed"]
[connection signal="timeout" from="TimerReimportIcons" to="." method="_on_timer_reimport_icons_timeout"]

View File

@ -0,0 +1,266 @@
# toolbar_menu.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends MenuButton
enum MENU_ID {
TOOL_FOLLOW_NORMAL,
TOOL_SHAPE_AIRBRUSH,
TOOL_SHAPE_PENCIL,
TOOL_SHAPE_ERASER,
TOOL_SNAP_TO_TERRAIN,
AUTO_CENTER_POSITION,
CAST_SHADOW,
BAKE_HEIGHT_MAP,
GLOBAL_PARAMETERS,
CLEAR_ALL,
HELP_ABOUT,
RECALCULATE_AABB,
}
enum MENU_SHAPE_ID {
TOOL_SHAPE_SPHERE,
TOOL_SHAPE_CYLINDER,
TOOL_SHAPE_CYLINDER_INF_H,
TOOL_SHAPE_BOX,
TOOL_SHAPE_BOX_INF_H,
}
var _plugin: EditorPlugin = null
var _grass_selected = null
var _tools_menu: PopupMenu = null
var _airbrush_shape_menu: PopupMenu = null
var _pencil_shape_menu: PopupMenu = null
var _eraser_shape_menu: PopupMenu = null
func set_plugin(plugin :EditorPlugin) -> void:
var popup := get_popup()
_plugin = plugin
_airbrush_shape_menu = PopupMenu.new()
_airbrush_shape_menu.name = &"AirbrushShapeMenu"
_airbrush_shape_menu.add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_airbrush_shape_menu.add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_pencil_shape_menu = PopupMenu.new()
_pencil_shape_menu.name = &"PencilShapeMenu"
_pencil_shape_menu.add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_pencil_shape_menu.add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_eraser_shape_menu = PopupMenu.new()
_eraser_shape_menu.name = &"EraserShapeMenu"
_eraser_shape_menu.add_radio_check_item("Sphere", MENU_SHAPE_ID.TOOL_SHAPE_SPHERE)
_eraser_shape_menu.add_radio_check_item("Cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
_eraser_shape_menu.add_radio_check_item("Infinite vertical cylinder", MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H)
_eraser_shape_menu.add_radio_check_item("Box", MENU_SHAPE_ID.TOOL_SHAPE_BOX)
_eraser_shape_menu.add_radio_check_item("Infinite vertical box", MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H)
_tools_menu = PopupMenu.new()
_tools_menu.add_child(_airbrush_shape_menu)
_tools_menu.add_child(_pencil_shape_menu)
_tools_menu.add_child(_eraser_shape_menu)
_tools_menu.name = &"ToolsMenu"
_tools_menu.add_check_item("Follow terrain's normal", MENU_ID.TOOL_FOLLOW_NORMAL)
_tools_menu.add_submenu_item("Airbrush shape", "AirbrushShapeMenu")
_tools_menu.add_submenu_item("Pencil shape", "PencilShapeMenu")
_tools_menu.add_submenu_item("Eraser shape", "EraserShapeMenu")
_tools_menu.add_separator()
_tools_menu.add_item("Snap to terrain", MENU_ID.TOOL_SNAP_TO_TERRAIN)
popup.add_child(_tools_menu)
popup.clear()
popup.add_submenu_item("Tools", "ToolsMenu")
popup.add_separator()
popup.add_item("Auto center position", MENU_ID.AUTO_CENTER_POSITION)
popup.add_item("Recalculate custom AABB", MENU_ID.RECALCULATE_AABB)
popup.add_item("Bake height map", MENU_ID.BAKE_HEIGHT_MAP)
popup.add_check_item("Cast shadow", MENU_ID.CAST_SHADOW)
popup.add_item("Global parameters", MENU_ID.GLOBAL_PARAMETERS)
popup.add_separator()
popup.add_item("Clear all", MENU_ID.CLEAR_ALL)
popup.add_separator()
popup.add_item("About SimpleGrassTextured", MENU_ID.HELP_ABOUT)
popup.id_pressed.connect(_on_sgt_menu_button)
_tools_menu.id_pressed.connect(_on_sgt_tools_menu_button)
_airbrush_shape_menu.id_pressed.connect(_on_sgt_shape_menu_pressed.bind("airbrush", _airbrush_shape_menu))
_pencil_shape_menu.id_pressed.connect(_on_sgt_shape_menu_pressed.bind("pencil", _pencil_shape_menu))
_eraser_shape_menu.id_pressed.connect(_on_sgt_shape_menu_pressed.bind("eraser", _eraser_shape_menu))
about_to_popup.connect(_on_about_to_popup)
func set_current_grass(grass_selected) -> void:
_grass_selected = grass_selected
if grass_selected == null:
return
var popup := get_popup()
if grass_selected.cast_shadow == GeometryInstance3D.SHADOW_CASTING_SETTING_OFF:
popup.set_item_checked(popup.get_item_index(MENU_ID.CAST_SHADOW), false)
else:
popup.set_item_checked(popup.get_item_index(MENU_ID.CAST_SHADOW), true)
if _grass_selected.baked_height_map != null:
popup.set_item_text(popup.get_item_index(MENU_ID.BAKE_HEIGHT_MAP), "Bake height map (already baked)")
popup.set_item_disabled(popup.get_item_index(MENU_ID.BAKE_HEIGHT_MAP), true)
else:
popup.set_item_text(popup.get_item_index(MENU_ID.BAKE_HEIGHT_MAP), "Bake height map")
popup.set_item_disabled(popup.get_item_index(MENU_ID.BAKE_HEIGHT_MAP), false)
_tools_menu.set_item_checked(_tools_menu.get_item_index(MENU_ID.TOOL_FOLLOW_NORMAL), _grass_selected.sgt_follow_normal)
for tool_name in _grass_selected.sgt_tool_shape:
match tool_name:
"airbrush":
_update_shape_menu_from_grass(_airbrush_shape_menu, _grass_selected.sgt_tool_shape[tool_name])
"pencil":
_update_shape_menu_from_grass(_pencil_shape_menu, _grass_selected.sgt_tool_shape[tool_name])
"eraser":
_update_shape_menu_from_grass(_eraser_shape_menu, _grass_selected.sgt_tool_shape[tool_name])
func _update_shape_menu_from_grass(popupmenu: PopupMenu, plugin_id_shape: int) -> void:
for i in popupmenu.item_count:
popupmenu.set_item_checked(i, false)
var idx := -1
match _plugin.get_tool_shape_name(plugin_id_shape):
"sphere":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_SPHERE)
"cylinder":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER)
"cylinder_inf_h":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H)
"box":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_BOX)
"box_inf_h":
idx = popupmenu.get_item_index(MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H)
_:
idx = -1
if idx == -1:
return
popupmenu.set_item_checked(idx, true)
func _on_sgt_menu_button(id :int) -> void:
if _grass_selected == null:
return
match id:
MENU_ID.AUTO_CENTER_POSITION:
_plugin.get_undo_redo().create_action(_grass_selected.name + " Auto Center Position")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"global_position", _grass_selected.global_position)
_grass_selected.auto_center_position()
_plugin.get_undo_redo().add_do_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"global_position", _grass_selected.global_position)
_plugin.get_undo_redo().commit_action()
MENU_ID.RECALCULATE_AABB:
_plugin.get_undo_redo().create_action(_grass_selected.name + " Recalculate Custom AABB")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"custom_aabb", _grass_selected.custom_aabb)
_grass_selected.recalculate_custom_aabb()
_plugin.get_undo_redo().add_do_property(_grass_selected, &"custom_aabb", _grass_selected.custom_aabb)
_plugin.get_undo_redo().commit_action()
MENU_ID.CAST_SHADOW:
_plugin.get_undo_redo().create_action(_grass_selected.name + " Toogle Cast Shadow")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"cast_shadow", _grass_selected.cast_shadow)
for child in _grass_selected.get_children():
if child.has_meta(&"SimpleGrassTexturedRegion"):
_plugin.get_undo_redo().add_undo_property(child, &"cast_shadow", child.cast_shadow)
if _grass_selected.cast_shadow == GeometryInstance3D.SHADOW_CASTING_SETTING_OFF:
_grass_selected.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_ON
else:
_grass_selected.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF
_plugin.get_undo_redo().add_do_property(_grass_selected, &"cast_shadow", _grass_selected.cast_shadow)
for child in _grass_selected.get_children():
if child.has_meta(&"SimpleGrassTexturedRegion"):
child.cast_shadow = _grass_selected.cast_shadow
_plugin.get_undo_redo().add_do_property(child, &"cast_shadow", child.cast_shadow)
_plugin.get_undo_redo().commit_action()
MENU_ID.BAKE_HEIGHT_MAP:
_grass_selected.bake_height_map()
MENU_ID.GLOBAL_PARAMETERS:
var _global_parameters = load("res://addons/simplegrasstextured/gui/global_parameters.tscn").instantiate()
get_window().add_child(_global_parameters)
_global_parameters.popup_centered()
MENU_ID.CLEAR_ALL:
var win = load("res://addons/simplegrasstextured/gui/clear_all_confirmation_dialog.tscn").instantiate()
get_window().add_child(win)
win.confirmed.connect(_on_clear_all_confirmation_dialog_confirmed)
win.popup_centered()
MENU_ID.HELP_ABOUT:
var win = load("res://addons/simplegrasstextured/gui/about.tscn").instantiate()
get_window().add_child(win)
win.popup_centered()
func _on_sgt_tools_menu_button(id :int) -> void:
if _grass_selected == null:
return
match id:
MENU_ID.TOOL_FOLLOW_NORMAL:
var idx := _tools_menu.get_item_index(id)
_tools_menu.set_item_checked(idx, not _tools_menu.is_item_checked(idx))
_grass_selected.sgt_follow_normal = _tools_menu.is_item_checked(idx)
MENU_ID.TOOL_SNAP_TO_TERRAIN:
_plugin.get_undo_redo().create_action(_grass_selected.name + " Snap to terrain")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"multimesh", _grass_selected.multimesh.duplicate())
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"global_position", _grass_selected.global_position)
_grass_selected.snap_to_terrain()
_plugin.get_undo_redo().add_do_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"global_position", _grass_selected.global_position)
_plugin.get_undo_redo().commit_action()
func _on_sgt_shape_menu_pressed(id: int, tool_name: String, popupmenu: PopupMenu) -> void:
if _grass_selected == null:
return
for i in popupmenu.item_count:
popupmenu.set_item_checked(i, false)
var idx := popupmenu.get_item_index(id)
if idx == -1:
return
popupmenu.set_item_checked(idx, true)
var shape_name := ""
match id:
MENU_SHAPE_ID.TOOL_SHAPE_SPHERE:
shape_name = "sphere"
MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER:
shape_name = "cylinder"
MENU_SHAPE_ID.TOOL_SHAPE_CYLINDER_INF_H:
shape_name = "cylinder_inf_h"
MENU_SHAPE_ID.TOOL_SHAPE_BOX:
shape_name = "box"
MENU_SHAPE_ID.TOOL_SHAPE_BOX_INF_H:
shape_name = "box_inf_h"
_plugin.set_tool_shape(tool_name, shape_name)
func _on_about_to_popup() -> void:
set_current_grass(_grass_selected)
func _on_clear_all_confirmation_dialog_confirmed() -> void:
if _grass_selected == null:
return
_plugin.get_undo_redo().create_action(_grass_selected.name + " Clear All Grass")
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_undo_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_grass_selected.clear_all()
_plugin.get_undo_redo().add_do_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
_plugin.get_undo_redo().add_do_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_plugin.get_undo_redo().commit_action()

View File

@ -0,0 +1 @@
uid://bk4wcnwns36qk

View File

@ -0,0 +1,31 @@
[gd_scene load_steps=2 format=3 uid="uid://der2mmxtog2mt"]
[ext_resource type="Script" uid="uid://bk4wcnwns36qk" path="res://addons/simplegrasstextured/gui/toolbar_menu.gd" id="1_mmgcy"]
[node name="ToolbarMenu" type="MenuButton"]
item_count = 11
popup/item_0/text = "Tools"
popup/item_0/id = 0
popup/item_1/id = -1
popup/item_1/separator = true
popup/item_2/text = "Auto center position"
popup/item_2/id = 1
popup/item_3/text = "Recalculate custom AABB"
popup/item_3/id = 7
popup/item_4/text = "Bake height map"
popup/item_4/checkable = 1
popup/item_4/id = 3
popup/item_5/text = "Cast shadow"
popup/item_5/checkable = 1
popup/item_5/id = 2
popup/item_6/text = "Global parameters"
popup/item_6/id = 4
popup/item_7/id = -1
popup/item_7/separator = true
popup/item_8/text = "Clear all"
popup/item_8/id = 5
popup/item_9/id = -1
popup/item_9/separator = true
popup/item_10/text = "About SimpleGrassTextured"
popup/item_10/id = 6
script = ExtResource("1_mmgcy")

View File

@ -0,0 +1,33 @@
# toolbar_up.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends Control
func set_plugin(plugin :EditorPlugin) -> void:
$MenuButton.set_plugin(plugin)
func set_current_grass(grass) -> void:
$MenuButton.set_current_grass(grass)

View File

@ -0,0 +1 @@
uid://cdfq5jjtnlcwg

View File

@ -0,0 +1,17 @@
[gd_scene load_steps=4 format=3 uid="uid://bfy4swcp2kwpq"]
[ext_resource type="Script" uid="uid://cdfq5jjtnlcwg" path="res://addons/simplegrasstextured/gui/toolbar_up.gd" id="1_h2433"]
[ext_resource type="Texture2D" uid="uid://bsmcpupaj02v8" path="res://addons/simplegrasstextured/sgt_icon.svg" id="3_637xa"]
[ext_resource type="PackedScene" uid="uid://der2mmxtog2mt" path="res://addons/simplegrasstextured/gui/toolbar_menu.tscn" id="3_suvo1"]
[node name="SGTToolbarUp" type="HBoxContainer"]
anchors_preset = 10
anchor_right = 1.0
grow_horizontal = 2
script = ExtResource("1_h2433")
[node name="MenuButton" parent="." instance=ExtResource("3_suvo1")]
layout_mode = 2
text = "SimpleGrassTextured"
icon = ExtResource("3_637xa")
switch_on_hover = true

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,41 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dk6rqq2wmb45o"
path.s3tc="res://.godot/imported/motion.png-ac3355e19edb929f8c8fd048d57b67cc.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/simplegrasstextured/images/motion.png"
dest_files=["res://.godot/imported/motion.png-ac3355e19edb929f8c8fd048d57b67cc.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=1
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=7
roughness/src_normal="res://textures/fern_normal.tga"
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bovwvrkiruiat"
path="res://.godot/imported/motion_frame.png-9de9ce804cbe9c1e551304fef14b5bcc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/motion_frame.png"
dest_files=["res://.godot/imported/motion_frame.png-9de9ce804cbe9c1e551304fef14b5bcc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=false
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cho4etu1eahnx"
path="res://.godot/imported/normal.png-3aae11463fd3b57390bff030445d49dc.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/normal.png"
dest_files=["res://.godot/imported/normal.png-3aae11463fd3b57390bff030445d49dc.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=2
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=false
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://m77dlrqfdeef"
path="res://.godot/imported/normal_frame.png-be2572e361af9089032fcc292b3b150e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/normal_frame.png"
dest_files=["res://.godot/imported/normal_frame.png-be2572e361af9089032fcc292b3b150e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=false
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

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://35qfdmr0xvq3"
path="res://.godot/imported/pointer.png-292374924e7ba5a25097927bb55f262e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/pointer.png"
dest_files=["res://.godot/imported/pointer.png-292374924e7ba5a25097927bb55f262e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

View File

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bianpj3vn603i"
path="res://.godot/imported/pointer_rect.png-44cc567cfff567b7aef3c6a89033a7fa.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/pointer_rect.png"
dest_files=["res://.godot/imported/pointer_rect.png-44cc567cfff567b7aef3c6a89033a7fa.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_airbrush.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="4.3230131"
inkscape:cy="6.8812519"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443" />
</sodipodi:namedview>
<path
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 14,14 V 9 L 12,8 V 6 H 10 V 8 L 8,9 v 5 c 0,0 1,1 3,1 2,0 3,-1 3,-1"
id="path6579"
sodipodi:nodetypes="ccccccccsc" />
<path
style="color:#000000;fill:#ffffff;fill-opacity:0.7;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="M 2.4472656,1.1054687 C 1.7823165,0.77308034 1.0000236,1.2566028 1,2 v 8 c 2.38e-5,0.743397 0.7823166,1.226919 1.4472656,0.894531 l 6,-3.4999998 C 8.7860465,7.2250899 9.0000258,6.8787913 9,6.5 v -1 C 9.0000258,5.1212087 8.7860465,4.7749101 8.4472656,4.6054688 Z"
id="path6581"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:none;fill-opacity:0.75;fill-rule:nonzero;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:normal"
d="m 10,8 h 2"
id="path12110" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://c2kunxv5wepih"
path="res://.godot/imported/sgt_icon_airbrush.svg-fb575618de9fc813b83450db0db42f43.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_airbrush.svg"
dest_files=["res://.godot/imported/sgt_icon_airbrush.svg-fb575618de9fc813b83450db0db42f43.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_airbrush_dark.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="4.2956523"
inkscape:cy="6.9086128"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443" />
</sodipodi:namedview>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 14,14 V 9 L 12,8 V 6 H 10 V 8 L 8,9 v 5 c 0,0 1,1 3,1 2,0 3,-1 3,-1"
id="path6579"
sodipodi:nodetypes="ccccccccsc" />
<path
style="color:#000000;fill:#000000;fill-opacity:0.69999999;stroke-linecap:round;stroke-linejoin:round;-inkscape-stroke:none"
d="M 2.4472656,1.1054687 C 1.7823165,0.77308034 1.0000236,1.2566028 1,2 v 8 c 2.38e-5,0.743397 0.7823166,1.226919 1.4472656,0.894531 l 6,-3.4999998 C 8.7860465,7.2250899 9.0000258,6.8787913 9,6.5 v -1 C 9.0000258,5.1212087 8.7860465,4.7749101 8.4472656,4.6054688 Z"
id="path6581"
sodipodi:nodetypes="ccccccccc" />
<path
style="fill:none;fill-opacity:0.75;fill-rule:nonzero;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;paint-order:normal"
d="m 10,8 h 2"
id="path12110" />
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bx0eldcaeq06i"
path="res://.godot/imported/sgt_icon_airbrush_dark.svg-5225c2c5467c46128914e641f3987ecb.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_airbrush_dark.svg"
dest_files=["res://.godot/imported/sgt_icon_airbrush_dark.svg-5225c2c5467c46128914e641f3987ecb.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 8 16"
width="8"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_arrow_up.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="1.2585988"
inkscape:cy="6.8402107"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443"
originx="0"
originy="0" />
</sodipodi:namedview>
<path
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 6,4 4,2 2,4"
id="path1055"
sodipodi:nodetypes="ccc" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bda52g6s88x5d"
path="res://.godot/imported/sgt_icon_arrow_up.svg-43e0412f6c6d1db6334668717d304875.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_arrow_up.svg"
dest_files=["res://.godot/imported/sgt_icon_arrow_up.svg-43e0412f6c6d1db6334668717d304875.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 8 16"
width="8"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_arrow_up_dark.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="1.2585988"
inkscape:cy="6.8402107"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443"
originx="0"
originy="0" />
</sodipodi:namedview>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 6,4 4,2 2,4"
id="path1055"
sodipodi:nodetypes="ccc" />
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d3br7bufrmi53"
path="res://.godot/imported/sgt_icon_arrow_up_dark.svg-e4923556b0287e0e1b572ab51887d9b2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_arrow_up_dark.svg"
dest_files=["res://.godot/imported/sgt_icon_arrow_up_dark.svg-e4923556b0287e0e1b572ab51887d9b2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m8 1.2578125c-3.7137378 0-6.7421875 3.0284495-6.7421875 6.7421875s3.0284497 6.742188 6.7421875 6.742188c3.713738 0 6.742188-3.02845 6.742188-6.742188s-3.02845-6.7421875-6.742188-6.7421875zm0 1.6992187c2.79499 0 5.042969 2.2479781 5.042969 5.0429688 0 2.794991-2.247979 5.042969-5.042969 5.042969-2.7949904 0-5.0429688-2.247978-5.0429688-5.042969 0-2.7949907 2.2479784-5.0429687 5.0429688-5.0429688zm-1.5390625 1.3730469a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm4.7011715.9570313a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-2.5859371 1.7617187a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm-3.671875.4765625a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1zm2.8339843 2.7304684a1 1 0 0 0 -1 1 1 1 0 0 0 1 1 1 1 0 0 0 1-1 1 1 0 0 0 -1-1z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 904 B

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://tqiak77omdnr"
path="res://.godot/imported/sgt_icon_density.svg-9c44dd38d1d502aa0cebccf5e2c9d3d3.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_density.svg"
dest_files=["res://.godot/imported/sgt_icon_density.svg-9c44dd38d1d502aa0cebccf5e2c9d3d3.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,149 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16"
height="16"
viewBox="0 0 16 16"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="sgt_icon_density_dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="45.254834"
inkscape:cx="7.3141358"
inkscape:cy="7.9991455"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:snap-grids="true"
inkscape:snap-to-guides="true"
inkscape:snap-nodes="true"
inkscape:snap-intersection-paths="false"
inkscape:snap-page="false"
showguides="false"
scale-x="1"
inkscape:showpageshadow="0"
inkscape:deskcolor="#505050">
<inkscape:grid
type="xygrid"
id="grid2648" />
</sodipodi:namedview>
<defs
id="defs2">
<marker
style="overflow:visible"
id="marker2902"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS"
inkscape:isstock="true">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2900" />
</marker>
<marker
style="overflow:visible"
id="DotS"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS"
inkscape:isstock="true">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2683" />
</marker>
<marker
style="overflow:visible"
id="DotM"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotM"
inkscape:isstock="true">
<path
transform="matrix(0.4,0,0,0.4,2.96,0.4)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2680" />
</marker>
<marker
style="overflow:visible"
id="DotL"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotL"
inkscape:isstock="true">
<path
transform="matrix(0.8,0,0,0.8,5.92,0.8)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2677" />
</marker>
</defs>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1">
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3240"
cx="4.9035625"
cy="8.5259209"
r="1" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3240-3"
cx="6.460289"
cy="5.329946"
r="1" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3240-6"
cx="7.737596"
cy="11.256416"
r="1" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3240-7"
cx="8.5762997"
cy="8.0479622"
r="1" />
<circle
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3240-5"
cx="11.162725"
cy="6.2866859"
r="1" />
<ellipse
style="fill:none;stroke:#000000;stroke-width:1.7;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1557"
cx="8"
cy="8"
rx="5.8925533"
ry="5.8925538" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://laar7ny28dqe"
path="res://.godot/imported/sgt_icon_density_dark.svg-c4782bd2e0aa1f3996b84f700be97f61.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_density_dark.svg"
dest_files=["res://.godot/imported/sgt_icon_density_dark.svg-c4782bd2e0aa1f3996b84f700be97f61.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m2 2.5a1 1 0 0 0 -1 1v9a1 1 0 0 0 1 1 1 1 0 0 0 1-1v-4.5-4.5a1 1 0 0 0 -1-1zm1 5.5a1 1 0 0 0 .00586.044922 1.0001 1.0001 0 0 0 .019531.1464843 1 1 0 0 0 .023437.1054688 1.0001 1.0001 0 0 0 .056641.1328125 1 1 0 0 0 .041016.085937 1.0001 1.0001 0 0 0 .1464844.1914062l2 1.9999998a1 1 0 0 0 1.4140624 0 1 1 0 0 0 0-1.4140622l-.2929693-.2929684h3.171875l-.2929687.2929688a1 1 0 0 0 0 1.4140622 1 1 0 0 0 1.4140622 0l2-1.9999998a1.0001 1.0001 0 0 0 .152344-.1992187 1.0001 1.0001 0 0 0 .0059-.00781 1 1 0 0 0 .0039-.00977 1.0001 1.0001 0 0 0 .09375-.21875 1 1 0 0 0 .005825-.0214825 1.0001 1.0001 0 0 0 .03125-.25 1.0001 1.0001 0 0 0 -.0039-.078125 1 1 0 0 0 -.0039-.041016 1.0001 1.0001 0 0 0 -.0098-.064453 1.0001 1.0001 0 0 0 -.02148-.089844 1.0001 1.0001 0 0 0 -.0293-.087891 1.0001 1.0001 0 0 0 -.0332-.076172 1.0001 1.0001 0 0 0 -.0039-.00781 1 1 0 0 0 -.0039-.00781 1.0001 1.0001 0 0 0 -.03906-.068359 1 1 0 0 0 -.04297-.066406 1 1 0 0 0 -.04883-.060547 1.0001 1.0001 0 0 0 -.02149-.025391 1 1 0 0 0 -.03125-.033203l-.0332-.03125-1.966797-1.96875a1 1 0 0 0 -.707023-.292973 1 1 0 0 0 -.7070312.2929688 1 1 0 0 0 0 1.4140624l.2929687.2929688h-3.171875l.2929687-.2929688a1 1 0 0 0 0-1.4140624 1 1 0 0 0 -.7070312-.2929688 1 1 0 0 0 -.7070312.2929688l-2 2a1.0001 1.0001 0 0 0 -.1464844.1914062 1 1 0 0 0 -.0410156.0859375 1.0001 1.0001 0 0 0 -.0566407.1328125 1 1 0 0 0 -.0234375.1054688 1.0001 1.0001 0 0 0 -.0195312.1464843 1 1 0 0 0 -.0058594.0449219zm10 0v4.5a1 1 0 0 0 1 1 1 1 0 0 0 1-1v-9a1 1 0 0 0 -1-1 1 1 0 0 0 -1 1z" fill="#fffffe" stroke-linecap="round"/></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://d28dlfkm62t6e"
path="res://.godot/imported/sgt_icon_distance.svg-77e66b67426f6dd1ba80a0a28564d425.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_distance.svg"
dest_files=["res://.godot/imported/sgt_icon_distance.svg-77e66b67426f6dd1ba80a0a28564d425.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,139 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16"
height="16"
viewBox="0 0 16 16"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="sgt_icon_distance_dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="45.254834"
inkscape:cx="7.3362329"
inkscape:cy="7.9991455"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:snap-grids="true"
inkscape:snap-to-guides="true"
inkscape:snap-nodes="true"
inkscape:snap-intersection-paths="false"
inkscape:snap-page="false"
showguides="false"
scale-x="1"
inkscape:showpageshadow="0"
inkscape:deskcolor="#505050">
<inkscape:grid
type="xygrid"
id="grid2648"
spacingx="0.5"
spacingy="0.5"
originx="0"
originy="0" />
</sodipodi:namedview>
<defs
id="defs2">
<marker
style="overflow:visible"
id="marker2902"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS"
inkscape:isstock="true">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2900" />
</marker>
<marker
style="overflow:visible"
id="DotS"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS"
inkscape:isstock="true">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2683" />
</marker>
<marker
style="overflow:visible"
id="DotM"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotM"
inkscape:isstock="true">
<path
transform="matrix(0.4,0,0,0.4,2.96,0.4)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2680" />
</marker>
<marker
style="overflow:visible"
id="DotL"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotL"
inkscape:isstock="true">
<path
transform="matrix(0.8,0,0,0.8,5.92,0.8)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2677" />
</marker>
</defs>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 2,3.5 v 9"
id="path1053" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 14,3.5 v 9"
id="path1053-3" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 12,8 H 4"
id="path1053-6"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 6,10 4,8 6,6"
id="path2611"
sodipodi:nodetypes="ccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1;stroke-miterlimit:4;stroke-dasharray:none"
d="M 10,10 12,8 10,6"
id="path2611-5"
sodipodi:nodetypes="ccc" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dji5ff3k0qrgj"
path="res://.godot/imported/sgt_icon_distance_dark.svg-b4abdf7bea37b691aed1d750518c30f6.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_distance_dark.svg"
dest_files=["res://.godot/imported/sgt_icon_distance_dark.svg-b4abdf7bea37b691aed1d750518c30f6.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_eraser.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="3.9399613"
inkscape:cy="9.7541404"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443"
spacingx="0.5"
spacingy="0.5" />
</sodipodi:namedview>
<path
style="fill:#fdffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 2,14 H 3"
id="path17621"
sodipodi:nodetypes="cc" />
<path
style="fill:#fdffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="m 11,14 h 3"
id="path17623"
sodipodi:nodetypes="cc" />
<path
style="opacity:1;fill:#fdffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 11,9 14,6 10,2 7,5 Z"
id="path18360"
sodipodi:nodetypes="ccccc" />
<g
id="g26435">
<path
id="path18358"
style="color:#000000;fill:#ffffff;fill-rule:evenodd;stroke-linecap:round;-inkscape-stroke:none;fill-opacity:0.5"
d="M 4.5,6.0859375 2.2929688,8.2929688 c -0.3904235,0.390507 -0.3904235,1.0235554 0,1.4140624 l 4,3.9999998 c 0.3905299,0.390368 1.0235325,0.390368 1.4140624,0 C 8.4644892,12.949573 9.1316995,12.282363 9.9140625,11.5 Z"
sodipodi:nodetypes="ccccccc" />
</g>
<path
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="m 7,5 -0.5,0.5 4,4 L 11,9 Z"
id="path25695" />
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://brugf6rnck1tk"
path="res://.godot/imported/sgt_icon_eraser.svg-e97e67f5b13a9a2f6e85bac1e1474a98.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_eraser.svg"
dest_files=["res://.godot/imported/sgt_icon_eraser.svg-e97e67f5b13a9a2f6e85bac1e1474a98.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_eraser_dark.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="3.9126005"
inkscape:cy="9.7815012"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443"
spacingx="0.5"
spacingy="0.5" />
</sodipodi:namedview>
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 2,14 H 3"
id="path17621"
sodipodi:nodetypes="cc" />
<path
style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="m 11,14 h 3"
id="path17623"
sodipodi:nodetypes="cc" />
<path
style="opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 11,9 14,6 10,2 7,5 Z"
id="path18360"
sodipodi:nodetypes="ccccc" />
<g
id="g26435"
style="fill:#000000;fill-opacity:0.69999999">
<path
id="path18358"
style="color:#000000;fill:#000000;fill-rule:evenodd;stroke-linecap:round;-inkscape-stroke:none;fill-opacity:0.69999999"
d="M 4.5,6.0859375 2.2929688,8.2929688 c -0.3904235,0.390507 -0.3904235,1.0235554 0,1.4140624 l 4,3.9999998 c 0.3905299,0.390368 1.0235325,0.390368 1.4140624,0 C 8.4644892,12.949573 9.1316995,12.282363 9.9140625,11.5 Z"
sodipodi:nodetypes="ccccccc" />
</g>
<path
style="opacity:1;fill:none;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-opacity:1"
d="m 7,5 -0.5,0.5 4,4 L 11,9 Z"
id="path25695" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cig4ev2rdad8h"
path="res://.godot/imported/sgt_icon_eraser_dark.svg-343cd82d6b551fa833c0dca85237188a.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_eraser_dark.svg"
dest_files=["res://.godot/imported/sgt_icon_eraser_dark.svg-343cd82d6b551fa833c0dca85237188a.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_pen.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="4.3230131"
inkscape:cy="6.8538911"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443" />
</sodipodi:namedview>
<path
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 11,2 3,10 2,14 6,13 14,5 C 14,5 14,4 13,3 12,2 11,2 11,2 Z"
id="path344"
sodipodi:nodetypes="cccccsc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round"
d="M 2,14 3,13"
id="path996"
sodipodi:nodetypes="cc" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dtal42g10x1ay"
path="res://.godot/imported/sgt_icon_pen.svg-c2f5516279abcab0d8d8d2c717868248.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_pen.svg"
dest_files=["res://.godot/imported/sgt_icon_pen.svg-c2f5516279abcab0d8d8d2c717868248.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_pen_dark.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="4.2956523"
inkscape:cy="6.8812519"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443" />
</sodipodi:namedview>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 11,2 3,10 2,14 6,13 14,5 C 14,5 14,4 13,3 12,2 11,2 11,2 Z"
id="path344"
sodipodi:nodetypes="cccccsc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
d="M 2,14 3,13"
id="path996"
sodipodi:nodetypes="cc" />
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://toenq726k1ir"
path="res://.godot/imported/sgt_icon_pen_dark.svg-a5711368f0cbd52073eb63daeaabd1ee.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_pen_dark.svg"
dest_files=["res://.godot/imported/sgt_icon_pen_dark.svg-a5711368f0cbd52073eb63daeaabd1ee.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m7.8671875 1.2617188c-.3038907.00632-.6064267.033329-.90625.080078-1.5990577.2493296-3.108174 1.0692433-4.1953125 2.4082032-2.17427681 2.6779197-1.97068427 6.584609.4707031 9.021484 2.4413876 2.436875 6.3495439 2.634307 9.0234379.455078 2.473304-2.015748 3.166556-5.4762941 1.757812-8.2656245.145263-.2978322.226563-.6317035.226563-.9824219 0-1.2415259-1.024099-2.2636718-2.265625-2.2636718-.358888 0-.69912.086848-1.001954.2382812-.9863204-.4857269-2.0530329-.7133923-3.1093745-.6914062zm-.0234375 1.6953124c.094481-.00293.1885299-.0024.2832031 0 .5713259.014495 1.1447113.1264527 1.6933594.3398438-.068951.2154842-.1054689.4445687-.1054687.6816406 0 .3597626.08811.7002861.2402343 1.0039063l-1.5683593 1.5683593c-.1235716-.0334128-.2532324-.0507812-.3867188-.0507812-.822504 0-1.5.677496-1.5 1.5s.677496 1.5 1.5 1.5 1.5-.677496 1.5-1.5c0-.1334864-.0173684-.2631472-.0507812-.3867188l1.5820312-1.5820312c.289215.1354542.609367.2128906.947266.2128906.245718 0 .482647-.041258.705078-.1152344.80796 2.0115065.244046 4.3594878-1.498047 5.7792968-2.0095188 1.637762-4.9132637 1.491548-6.748047-.339844-1.8347832-1.8313906-1.9856033-4.7335483-.3515625-6.7460934.9574459-1.1792256 2.3405979-1.8213414 3.7578125-1.8652344zm4.134766.2675782c.425764 0 .755859.3281414.755859.7539062s-.330095.7558594-.755859.7558594c-.425765 0-.753907-.3300946-.753907-.7558594s.328142-.7539062.753907-.7539062z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://clm6vd2es8xm7"
path="res://.godot/imported/sgt_icon_radius.svg-e958950c7ee38ebcf9793340191c6248.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_radius.svg"
dest_files=["res://.godot/imported/sgt_icon_radius.svg-e958950c7ee38ebcf9793340191c6248.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,147 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="16"
height="16"
viewBox="0 0 16 16"
version="1.1"
id="svg5"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
sodipodi:docname="sgt_icon_radius_dark.svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<sodipodi:namedview
id="namedview7"
pagecolor="#505050"
bordercolor="#ffffff"
borderopacity="1"
inkscape:pageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="1"
inkscape:document-units="px"
showgrid="true"
inkscape:zoom="45.254834"
inkscape:cx="8.1538251"
inkscape:cy="8.5073785"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="layer1"
inkscape:snap-grids="true"
inkscape:snap-to-guides="true"
inkscape:snap-nodes="true"
inkscape:snap-intersection-paths="false"
inkscape:snap-page="false"
showguides="false"
inkscape:showpageshadow="0"
inkscape:deskcolor="#505050">
<inkscape:grid
type="xygrid"
id="grid2648" />
</sodipodi:namedview>
<defs
id="defs2">
<marker
style="overflow:visible"
id="marker2902"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS"
inkscape:isstock="true">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2900" />
</marker>
<marker
style="overflow:visible"
id="DotS"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotS"
inkscape:isstock="true">
<path
transform="matrix(0.2,0,0,0.2,1.48,0.2)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2683" />
</marker>
<marker
style="overflow:visible"
id="DotM"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotM"
inkscape:isstock="true">
<path
transform="matrix(0.4,0,0,0.4,2.96,0.4)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2680" />
</marker>
<marker
style="overflow:visible"
id="DotL"
refX="0"
refY="0"
orient="auto"
inkscape:stockid="DotL"
inkscape:isstock="true">
<path
transform="matrix(0.8,0,0,0.8,5.92,0.8)"
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
d="m -2.5,-1 c 0,2.76 -2.24,5 -5,5 -2.76,0 -5,-2.24 -5,-5 0,-2.76 2.24,-5 5,-5 2.76,0 5,2.24 5,5 z"
id="path2677" />
</marker>
</defs>
<g
inkscape:label="Capa 1"
inkscape:groupmode="layer"
id="layer1">
<path
style="fill:none;stroke:#000000;stroke-width:1.7;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path1557"
sodipodi:type="arc"
sodipodi:cx="8"
sodipodi:cy="8"
sodipodi:rx="5.8925533"
sodipodi:ry="5.8925538"
sodipodi:start="5.7018649"
sodipodi:end="5.2918593"
sodipodi:open="true"
sodipodi:arc-type="arc"
d="M 12.924633,4.7642341 A 5.8925533,5.8925538 0 0 1 11.72268,12.567695 5.8925533,5.8925538 0 0 1 3.8371917,12.170517 5.8925533,5.8925538 0 0 1 3.4254258,4.2857763 5.8925533,5.8925538 0 0 1 11.226649,3.069389" />
<path
style="fill:none;stroke:#000000;stroke-width:1.5;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="M 8,8 11,5"
id="path2292"
sodipodi:nodetypes="cc"
inkscape:transform-center-x="-2.9955123"
inkscape:transform-center-y="0.019454311" />
<circle
style="fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3240"
cx="8"
cy="8"
r="1"
inkscape:transform-center-x="0.0044877413"
inkscape:transform-center-y="0.019454311" />
<circle
style="fill:none;stroke:#000000;stroke-width:1.50944;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="path3242"
cx="11.979179"
cy="3.9791806"
r="1.50945"
inkscape:transform-center-x="-9.0167698"
inkscape:transform-center-y="0.029365079" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://ie4tguvft6x6"
path="res://.godot/imported/sgt_icon_radius_dark.svg-ea6d9b3ce54dd74a1591724bc7148489.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_radius_dark.svg"
dest_files=["res://.godot/imported/sgt_icon_radius_dark.svg-ea6d9b3ce54dd74a1591724bc7148489.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><sodipodi:namedview><inkscape:grid/></sodipodi:namedview><g fill="none" stroke="#fff" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"><path d="m4 14h10v-10z"/><path d="m3.5 8.5 5-5"/><path d="m9 6v-3h-3"/></g></svg>

After

Width:  |  Height:  |  Size: 314 B

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://24avwtyvf01r"
path="res://.godot/imported/sgt_icon_slope.svg-8e8415d31ce58509819b0e249773a098.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_slope.svg"
dest_files=["res://.godot/imported/sgt_icon_slope.svg-8e8415d31ce58509819b0e249773a098.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
height="16"
viewBox="0 0 16 16"
width="16"
version="1.1"
id="svg4"
sodipodi:docname="sgt_icon_slope_dark.svg"
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg">
<defs
id="defs8" />
<sodipodi:namedview
id="namedview6"
pagecolor="#505050"
bordercolor="#eeeeee"
borderopacity="1"
inkscape:showpageshadow="0"
inkscape:pageopacity="0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#505050"
showgrid="true"
inkscape:zoom="36.548582"
inkscape:cx="4.2956523"
inkscape:cy="6.8402107"
inkscape:window-width="1920"
inkscape:window-height="1008"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg4">
<inkscape:grid
type="xygrid"
id="grid443" />
</sodipodi:namedview>
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 4,14 H 14 V 4 Z"
id="path392"
sodipodi:nodetypes="cccc" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="m 3.5,8.5 5,-5"
id="path1053" />
<path
style="fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
d="M 9,6 V 3 H 6"
id="path1055"
sodipodi:nodetypes="ccc" />
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dde4j4w233rnv"
path="res://.godot/imported/sgt_icon_slope_dark.svg-d0a7497efb4393f6bf2567a427bd889e.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/images/sgt_icon_slope_dark.svg"
dest_files=["res://.godot/imported/sgt_icon_slope_dark.svg-d0a7497efb4393f6bf2567a427bd889e.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

View File

@ -0,0 +1,41 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://i566v0wqsfa"
path.s3tc="res://.godot/imported/wind_pattern.png-414bc96d501e14f1400565420145e185.s3tc.ctex"
metadata={
"imported_formats": ["s3tc_bptc"],
"vram_texture": true
}
[deps]
source_file="res://addons/simplegrasstextured/images/wind_pattern.png"
dest_files=["res://.godot/imported/wind_pattern.png-414bc96d501e14f1400565420145e185.s3tc.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=-3
roughness/src_normal="res://textures/fern_normal.tga"
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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

View File

@ -0,0 +1,7 @@
[plugin]
name="SimpleGrassTextured"
description="Create simple grass textured"
author="IcterusGames"
version="2.0.9"
script="plugin.gd"

View File

@ -0,0 +1,880 @@
# plugin.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@tool
extends EditorPlugin
const DEFAULT_POINTER_DEPTH := 10.0
enum EVENT_MOUSE {
EVENT_NONE,
EVENT_MOVE,
EVENT_CLICK,
}
enum TOOL {
NONE,
PENCIL,
AIRBRUSH,
ERASER
}
enum TOOL_SHAPE {
SPHERE,
CYLINDER,
CYLINDER_INF_H,
BOX,
BOX_INF_H
}
var _raycast_3d : RayCast3D = null
var _pointer_decal : Decal = null
var _pointer_img_circle = load("res://addons/simplegrasstextured/images/pointer.png")
var _pointer_img_rect = load("res://addons/simplegrasstextured/images/pointer_rect.png")
var _pointer_depth: int = DEFAULT_POINTER_DEPTH
var _pointer_rotate: bool = true
var _grass_selected = null
var _position_draw := Vector3.ZERO
var _normal_draw := Vector3.ZERO
var _object_draw : Object = null
var _edit_density := 25
var _edit_radius := 2.0
var _edit_slope := Vector2(0, 45)
var _edit_scale := Vector3.ONE
var _edit_rotation := 0.0
var _edit_rotation_rand := 1.0
var _edit_tool: TOOL = TOOL.AIRBRUSH : set = _on_set_tool
var _gui_toolbar = null
var _gui_toolbar_up = null
var _time_draw := 0
var _draw_paused := true
var _mouse_event := EVENT_MOUSE.EVENT_NONE
var _project_ray_origin := Vector3.INF
var _project_ray_normal := Vector3.INF
var _inspector_plugin : EditorInspectorPlugin = null
var _evaluate_draw_time: int = 100
var _prev_config := ""
var _custom_settings := [{
"name": "SimpleGrassTextured/General/default_terrain_physics_layer",
"type": TYPE_INT,
"hint": PROPERTY_HINT_LAYERS_3D_PHYSICS,
"hint_string": "",
"default": pow(2, 32) - 1,
"basic": true
},{
"name": "SimpleGrassTextured/General/evaluate_draw_time",
"type": TYPE_INT,
"hint": PROPERTY_HINT_ENUM,
"hint_string": "Slow:150,Normal:100,Fast:50,Very fast:25",
"default": 50,
"basic": false
},{
"name": "SimpleGrassTextured/General/interactive_resolution",
"type": TYPE_INT,
"hint": PROPERTY_HINT_ENUM,
"hint_string": "Low:256,High:512",
"default": 512,
"basic": false
},{
"name": "SimpleGrassTextured/General/interactive_resolution.android",
"type": TYPE_INT,
"hint": PROPERTY_HINT_ENUM,
"hint_string": "Low:256,High:512",
"default": 256,
"basic": false
},{
"name": "SimpleGrassTextured/General/interactive_resolution.ios",
"type": TYPE_INT,
"hint": PROPERTY_HINT_ENUM,
"hint_string": "Low:256,High:512",
"default": 256,
"basic": false
},{
"name": "SimpleGrassTextured/Shortcuts/airbrush_tool",
"type": TYPE_OBJECT,
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Shortcut",
"default": _create_shortcut(KEY_D),
"basic": true
},{
"name": "SimpleGrassTextured/Shortcuts/pencil_tool",
"type": TYPE_OBJECT,
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Shortcut",
"default": _create_shortcut(KEY_B),
"basic": true
},{
"name": "SimpleGrassTextured/Shortcuts/eraser_tool",
"type": TYPE_OBJECT,
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Shortcut",
"default": _create_shortcut(KEY_X),
"basic": true
},{
"name": "SimpleGrassTextured/Shortcuts/radius_increment",
"type": TYPE_OBJECT,
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Shortcut",
"default": _create_shortcut(KEY_BRACKETRIGHT),
"basic": true
},{
"name": "SimpleGrassTextured/Shortcuts/radius_decrement",
"type": TYPE_OBJECT,
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Shortcut",
"default": _create_shortcut(KEY_BRACKETLEFT),
"basic": true
},{
"name": "SimpleGrassTextured/Shortcuts/density_increment",
"type": TYPE_OBJECT,
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Shortcut",
"default": _create_shortcut(KEY_EQUAL),
"basic": true
},{
"name": "SimpleGrassTextured/Shortcuts/density_decrement",
"type": TYPE_OBJECT,
"hint": PROPERTY_HINT_RESOURCE_TYPE,
"hint_string": "Shortcut",
"default": _create_shortcut(KEY_MINUS),
"basic": true
}
]
func _enter_tree() -> void:
if not get_tree().has_user_signal(&"sgt_globals_params_changed"):
get_tree().add_user_signal(&"sgt_globals_params_changed")
_verify_global_shader_parameters()
_enable_shaders(true)
_init_default_project_settings()
_prev_config = _custom_config_memorize()
if ProjectSettings.has_signal(&"settings_changed"):
ProjectSettings.connect(&"settings_changed", _on_project_settings_changed)
# Must ensure the resource file of default_mesh.tres match the current Godot version
var default_mesh = load("res://addons/simplegrasstextured/default_mesh.tres")
if not default_mesh or not default_mesh.has_meta(&"GodotVersion") or default_mesh.get_meta(&"GodotVersion") != Engine.get_version_info()["string"]:
push_warning("SimpleGrassTextured, updating file res://addons/simplegrasstextured/default_mesh.tres")
default_mesh = null
var mesh_builder = load("res://addons/simplegrasstextured/default_mesh_builder.gd").new()
mesh_builder.rebuild_and_save_default_mesh()
default_mesh = load("res://addons/simplegrasstextured/default_mesh.tres")
if default_mesh:
default_mesh.emit_changed()
print("SimpleGrassTextured, file updated successfully res://addons/simplegrasstextured/default_mesh.tres")
else:
push_error("SimpleGrassTextured, error updating file res://addons/simplegrasstextured/default_mesh.tres")
add_custom_type(
"SimpleGrassTextured",
"MultiMeshInstance3D",
load("res://addons/simplegrasstextured/grass.gd"),
load("res://addons/simplegrasstextured/sgt_icon.svg")
)
_gui_toolbar = load("res://addons/simplegrasstextured/gui/toolbar.tscn").instantiate()
_gui_toolbar.visible = false
add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, _gui_toolbar)
_gui_toolbar.set_plugin(self)
_gui_toolbar_up = load("res://addons/simplegrasstextured/gui/toolbar_up.tscn").instantiate()
_gui_toolbar_up.visible = false
add_control_to_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, _gui_toolbar_up)
_gui_toolbar_up.set_plugin(self)
_inspector_plugin = load("res://addons/simplegrasstextured/sgt_inspector.gd").new()
add_inspector_plugin(_inspector_plugin)
_raycast_3d = RayCast3D.new()
_raycast_3d.collision_mask = pow(2, 32) - 1
_raycast_3d.visible = false
_pointer_decal = Decal.new()
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_circle)
_pointer_decal.visible = false
_pointer_decal.extents = Vector3(_edit_radius, _pointer_depth, _edit_radius)
_pointer_decal.upper_fade = 0
_pointer_decal.lower_fade = 0
add_child(_raycast_3d)
add_child(_pointer_decal)
_gui_toolbar.slider_radius.value_changed.connect(_on_slider_radius_value_changed)
_gui_toolbar.slider_density.value_changed.connect(_on_slider_density_value_changed)
_gui_toolbar.button_airbrush.toggled.connect(_on_button_airbrush_toggled)
_gui_toolbar.button_pencil.toggled.connect(_on_button_pencil_toggled)
_gui_toolbar.button_eraser.toggled.connect(_on_button_eraser_toggled)
_gui_toolbar.edit_slope_range.value_changed.connect(_on_edit_slope_range_changed)
_gui_toolbar.edit_scale.value_changed.connect(_on_edit_scale_value_changed)
_gui_toolbar.edit_rotation.value_changed.connect(_on_edit_rotation_value_changed)
_gui_toolbar.edit_rotation_rand.value_changed.connect(_on_edit_rotation_rand_value_changed)
_gui_toolbar.edit_distance.value_changed.connect(_on_edit_distance_value_changed)
_edit_tool = TOOL.AIRBRUSH
func _exit_tree() -> void:
var current_config := _custom_config_memorize()
if current_config != _prev_config:
# Force save settings if some shortcut has been changed
ProjectSettings.save()
_grass_selected = null
_raycast_3d.queue_free()
_pointer_decal.queue_free()
remove_custom_type("SimpleGrassTextured")
remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_BOTTOM, _gui_toolbar)
_gui_toolbar.queue_free()
remove_control_from_container(EditorPlugin.CONTAINER_SPATIAL_EDITOR_MENU, _gui_toolbar_up)
_gui_toolbar_up.queue_free()
if _inspector_plugin != null:
remove_inspector_plugin(_inspector_plugin)
func _enable_plugin() -> void:
_verify_global_shader_parameters()
func _disable_plugin() -> void:
_enable_shaders(false)
remove_autoload_singleton("SimpleGrass")
if ProjectSettings.has_setting("shader_globals/sgt_legacy_renderer"):
ProjectSettings.set_setting("shader_globals/sgt_legacy_renderer", null)
if ProjectSettings.has_setting("shader_globals/sgt_player_position"):
ProjectSettings.set_setting("shader_globals/sgt_player_position", null)
if ProjectSettings.has_setting("shader_globals/sgt_player_mov"):
ProjectSettings.set_setting("shader_globals/sgt_player_mov", null)
if ProjectSettings.has_setting("shader_globals/sgt_normal_displacement"):
ProjectSettings.set_setting("shader_globals/sgt_normal_displacement", null)
if ProjectSettings.has_setting("shader_globals/sgt_motion_texture"):
ProjectSettings.set_setting("shader_globals/sgt_motion_texture", null)
if ProjectSettings.has_setting("shader_globals/sgt_wind_direction"):
ProjectSettings.set_setting("shader_globals/sgt_wind_direction", null)
if ProjectSettings.has_setting("shader_globals/sgt_wind_movement"):
ProjectSettings.set_setting("shader_globals/sgt_wind_movement", null)
if ProjectSettings.has_setting("shader_globals/sgt_wind_strength"):
ProjectSettings.set_setting("shader_globals/sgt_wind_strength", null)
if ProjectSettings.has_setting("shader_globals/sgt_wind_turbulence"):
ProjectSettings.set_setting("shader_globals/sgt_wind_turbulence", null)
if ProjectSettings.has_setting("shader_globals/sgt_wind_pattern"):
ProjectSettings.set_setting("shader_globals/sgt_wind_pattern", null)
# Remove custom settings from Project Settings
for entry in _custom_settings:
if ProjectSettings.has_setting(entry["name"]):
ProjectSettings.set_setting(entry["name"], null)
# Fix editor crash when disable plugin while SimpleGrassTextured node is selected
_grass_selected = null
var editor = get_editor_interface()
if editor != null:
var scene_root = editor.get_edited_scene_root()
if scene_root != null:
editor.edit_node(scene_root)
var selection = editor.get_selection()
if selection != null:
selection.clear()
func _get_plugin_name() -> String:
return "SimpleGrassTextured"
func _handles(object) -> bool:
if object != null and object.has_meta("SimpleGrassTextured") and object.visible:
_grass_selected = object
_update_gui()
_update_pointer()
return true
_grass_selected = null
return false
func _edit(object) -> void:
_grass_selected = object
_update_gui()
_update_pointer()
func _make_visible(visible : bool) -> void:
if visible:
if _grass_selected != null:
_update_gui()
_gui_toolbar.visible = true
_gui_toolbar_up.visible = true
else:
_gui_toolbar.visible = false
_gui_toolbar_up.visible = false
_pointer_decal.visible = false
_grass_selected = null
_gui_toolbar.set_current_grass(null)
_gui_toolbar_up.set_current_grass(null)
func _physics_process(_delta) -> void:
if _mouse_event == EVENT_MOUSE.EVENT_CLICK:
_raycast_3d.global_transform.origin = _project_ray_origin
_raycast_3d.global_transform.basis.y = _project_ray_normal
_raycast_3d.target_position = Vector3(0, 100000, 0)
_raycast_3d.collision_mask = _grass_selected.collision_mask
_raycast_3d.force_raycast_update()
if _raycast_3d.is_colliding():
_position_draw = _raycast_3d.get_collision_point()
_normal_draw = _raycast_3d.get_collision_normal()
_object_draw = _raycast_3d.get_collider()
_eval_brush()
_time_draw = Time.get_ticks_msec()
_draw_paused = false
else:
_time_draw = 0
_draw_paused = true
_object_draw = null
_mouse_event = EVENT_MOUSE.EVENT_NONE
elif _mouse_event == EVENT_MOUSE.EVENT_MOVE:
_raycast_3d.global_transform.origin = _project_ray_origin
_raycast_3d.global_transform.basis.y = _project_ray_normal
_raycast_3d.target_position = Vector3(0, 100000, 0)
_raycast_3d.collision_mask = _grass_selected.collision_mask
_raycast_3d.force_raycast_update()
if ( not _raycast_3d.is_colliding()
or ( _object_draw != null and _raycast_3d.get_collider() != _object_draw )):
_pointer_decal.visible = false
_draw_paused = true
_mouse_event = EVENT_MOUSE.EVENT_NONE
return
else:
_draw_paused = false
_position_draw = _raycast_3d.get_collision_point()
_normal_draw = _raycast_3d.get_collision_normal()
if _pointer_rotate:
var trans := Transform3D()
if abs(_normal_draw.z) == 1:
trans.basis.x = Vector3(1,0,0)
trans.basis.y = Vector3(0,0,_normal_draw.z)
trans.basis.z = Vector3(0,_normal_draw.z,0)
else:
trans.basis.y = _normal_draw
trans.basis.x = _normal_draw.cross(trans.basis.z)
trans.basis.z = trans.basis.x.cross(_normal_draw)
trans.basis = trans.basis.orthonormalized()
trans.origin = _position_draw
_pointer_decal.global_transform = trans
else:
_pointer_decal.global_position = _position_draw
_pointer_decal.rotation = Vector3.ZERO
_pointer_decal.extents = Vector3(_edit_radius, _pointer_depth, _edit_radius)
_pointer_decal.visible = _edit_tool != TOOL.NONE
_mouse_event = EVENT_MOUSE.EVENT_NONE
if _time_draw > 0:
if not _draw_paused:
if Time.get_ticks_msec() - _time_draw >= _evaluate_draw_time:
_time_draw = Time.get_ticks_msec()
_eval_brush()
func _forward_3d_gui_input(viewport_camera: Camera3D, event: InputEvent) -> int:
if _grass_selected == null:
return EditorPlugin.AFTER_GUI_INPUT_PASS
if _grass_selected.multimesh != null:
_gui_toolbar.label_stats.text = "Count: " + str(_grass_selected.multimesh.instance_count)
if event is InputEventMouseButton:
if event.button_index == MOUSE_BUTTON_LEFT:
if _edit_tool == TOOL.NONE:
return EditorPlugin.AFTER_GUI_INPUT_PASS
if event.pressed:
if _edit_tool == TOOL.AIRBRUSH:
get_undo_redo().create_action(_grass_selected.name + " Airbrush tool")
elif _edit_tool == TOOL.PENCIL:
get_undo_redo().create_action(_grass_selected.name + " Pencil tool")
elif _edit_tool == TOOL.ERASER:
get_undo_redo().create_action(_grass_selected.name + " Eraser tool")
else:
get_undo_redo().create_action(_grass_selected.name)
get_undo_redo().add_undo_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
get_undo_redo().add_undo_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
_project_ray_origin = viewport_camera.project_ray_origin(event.position)
_project_ray_normal = viewport_camera.project_ray_normal(event.position)
_mouse_event = EVENT_MOUSE.EVENT_CLICK
else:
get_undo_redo().add_do_property(_grass_selected, &"baked_height_map", _grass_selected.baked_height_map)
get_undo_redo().add_do_property(_grass_selected, &"multimesh", _grass_selected.multimesh)
get_undo_redo().commit_action()
_time_draw = 0
_object_draw = null
_mouse_event = EVENT_MOUSE.EVENT_NONE
return EditorPlugin.AFTER_GUI_INPUT_STOP
if event is InputEventMouseMotion:
if _mouse_event != EVENT_MOUSE.EVENT_CLICK:
_project_ray_origin = viewport_camera.project_ray_origin(event.position)
_project_ray_normal = viewport_camera.project_ray_normal(event.position)
_mouse_event = EVENT_MOUSE.EVENT_MOVE
return EditorPlugin.AFTER_GUI_INPUT_PASS
func _verify_global_shader_parameters() -> void:
if not ProjectSettings.has_setting("shader_globals/sgt_legacy_renderer"):
ProjectSettings.set_setting("shader_globals/sgt_legacy_renderer", {
"type": "int",
"value": 0
})
if RenderingServer.global_shader_parameter_get("sgt_legacy_renderer") == null:
var using_legacy_renderer = ProjectSettings.get_setting_with_override("rendering/renderer/rendering_method") == "gl_compatibility"
RenderingServer.global_shader_parameter_add("sgt_legacy_renderer", RenderingServer.GLOBAL_VAR_TYPE_INT, 1 if using_legacy_renderer else 0)
if not ProjectSettings.has_setting("shader_globals/sgt_player_position"):
ProjectSettings.set_setting("shader_globals/sgt_player_position", {
"type": "vec3",
"value": Vector3(1000000, 1000000, 1000000)
})
if RenderingServer.global_shader_parameter_get("sgt_player_position") == null:
RenderingServer.global_shader_parameter_add("sgt_player_position", RenderingServer.GLOBAL_VAR_TYPE_VEC3, Vector3(1000000,1000000,1000000))
if not ProjectSettings.has_setting("shader_globals/sgt_player_mov"):
ProjectSettings.set_setting("shader_globals/sgt_player_mov", {
"type": "vec3",
"value": Vector3.ZERO
})
if RenderingServer.global_shader_parameter_get("sgt_player_mov") == null:
RenderingServer.global_shader_parameter_add("sgt_player_mov", RenderingServer.GLOBAL_VAR_TYPE_VEC3, Vector3.ZERO)
if not ProjectSettings.has_setting("shader_globals/sgt_normal_displacement"):
ProjectSettings.set_setting("shader_globals/sgt_normal_displacement", {
"type": "sampler2D",
"value": "res://addons/simplegrasstextured/images/normal.png"
})
if RenderingServer.global_shader_parameter_get("sgt_normal_displacement") == null:
RenderingServer.global_shader_parameter_add("sgt_normal_displacement", RenderingServer.GLOBAL_VAR_TYPE_SAMPLER2D, load("res://addons/simplegrasstextured/images/normal.png"))
if not ProjectSettings.has_setting("shader_globals/sgt_motion_texture"):
ProjectSettings.set_setting("shader_globals/sgt_motion_texture", {
"type": "sampler2D",
"value": "res://addons/simplegrasstextured/images/motion.png"
})
if RenderingServer.global_shader_parameter_get("sgt_motion_texture") == null:
RenderingServer.global_shader_parameter_add("sgt_motion_texture", RenderingServer.GLOBAL_VAR_TYPE_SAMPLER2D, load("res://addons/simplegrasstextured/images/motion.png"))
if not ProjectSettings.has_setting("shader_globals/sgt_wind_direction"):
ProjectSettings.set_setting("shader_globals/sgt_wind_direction", {
"type": "vec3",
"value": Vector3(1, 0, 0)
})
if RenderingServer.global_shader_parameter_get("sgt_wind_direction") == null:
RenderingServer.global_shader_parameter_add("sgt_wind_direction", RenderingServer.GLOBAL_VAR_TYPE_VEC3, Vector3(1, 0, 0))
if not ProjectSettings.has_setting("shader_globals/sgt_wind_movement"):
ProjectSettings.set_setting("shader_globals/sgt_wind_movement", {
"type": "vec3",
"value": Vector2.ZERO
})
if RenderingServer.global_shader_parameter_get("sgt_wind_movement") == null:
RenderingServer.global_shader_parameter_add("sgt_wind_movement", RenderingServer.GLOBAL_VAR_TYPE_VEC3, Vector3.ZERO)
if not ProjectSettings.has_setting("shader_globals/sgt_wind_strength"):
ProjectSettings.set_setting("shader_globals/sgt_wind_strength", {
"type": "float",
"value": 0.15
})
if RenderingServer.global_shader_parameter_get("sgt_wind_strength") == null:
RenderingServer.global_shader_parameter_add("sgt_wind_strength", RenderingServer.GLOBAL_VAR_TYPE_FLOAT, 0.15)
if not ProjectSettings.has_setting("shader_globals/sgt_wind_turbulence"):
ProjectSettings.set_setting("shader_globals/sgt_wind_turbulence", {
"type": "float",
"value": 1.0
})
if RenderingServer.global_shader_parameter_get("sgt_wind_turbulence") == null:
RenderingServer.global_shader_parameter_add("sgt_wind_turbulence", RenderingServer.GLOBAL_VAR_TYPE_FLOAT, 1.0)
if not ProjectSettings.has_setting("shader_globals/sgt_wind_pattern"):
ProjectSettings.set_setting("shader_globals/sgt_wind_pattern", {
"type": "sampler2D",
"value": "res://addons/simplegrasstextured/images/wind_pattern.png"
})
if RenderingServer.global_shader_parameter_get("sgt_wind_pattern") == null:
RenderingServer.global_shader_parameter_add("sgt_wind_pattern", RenderingServer.GLOBAL_VAR_TYPE_SAMPLER2D, load("res://addons/simplegrasstextured/images/wind_pattern.png"))
if not ProjectSettings.has_setting("autoload/SimpleGrass"):
add_autoload_singleton("SimpleGrass", "res://addons/simplegrasstextured/singleton.tscn")
func _enable_shaders(enable :bool) -> void:
if enable:
var dir := DirAccess.open("res://")
var scan := false
if dir.file_exists("res://addons/simplegrasstextured/shaders/.gdignore"):
dir.remove("res://addons/simplegrasstextured/shaders/.gdignore")
scan = true
if dir.file_exists("res://addons/simplegrasstextured/materials/.gdignore"):
dir.remove("res://addons/simplegrasstextured/materials/.gdignore")
scan = true
if scan:
var editor = get_editor_interface()
editor.get_resource_filesystem().scan.call_deferred()
else:
var file := FileAccess.open("res://addons/simplegrasstextured/shaders/.gdignore", FileAccess.WRITE)
file.close()
file = FileAccess.open("res://addons/simplegrasstextured/materials/.gdignore", FileAccess.WRITE)
file.close()
var editor = get_editor_interface()
editor.get_resource_filesystem().scan.call_deferred()
func _create_shortcut(keycode :Key) -> Shortcut:
var shortcut := Shortcut.new()
var key := InputEventKey.new()
key.keycode = keycode
key.pressed = true
shortcut.events.append(key)
return shortcut
func _custom_config_memorize() -> String:
var config := ConfigFile.new()
for entry in _custom_settings:
config.set_value("s", entry["name"], ProjectSettings.get_setting_with_override(entry["name"]))
return config.encode_to_text()
func get_custom_setting(name :String) -> Variant:
if ProjectSettings.has_setting(name):
return ProjectSettings.get_setting_with_override(name)
for entry in _custom_settings:
if entry["name"] != name:
continue
return entry["default"]
push_error("SimpleGrassTextured, setting not found: ", name)
return null
func set_tool(tool_name: String) -> void:
match tool_name:
"none":
_edit_tool = TOOL.NONE
"airbrush":
_edit_tool = TOOL.AIRBRUSH
"pencil":
_edit_tool = TOOL.PENCIL
"eraser":
_edit_tool = TOOL.ERASER
func set_tool_shape(tool_name: String, shape_name: String) -> void:
if _grass_selected == null:
return
var shape_id: TOOL_SHAPE
match shape_name:
"sphere":
shape_id = TOOL_SHAPE.SPHERE
"cylinder":
shape_id = TOOL_SHAPE.CYLINDER
"cylinder_inf_h":
shape_id = TOOL_SHAPE.CYLINDER_INF_H
"box":
shape_id = TOOL_SHAPE.BOX
"box_inf_h":
shape_id = TOOL_SHAPE.BOX_INF_H
_grass_selected.sgt_tool_shape[tool_name] = shape_id
_update_pointer()
func get_tool_shape_name(shape_id: int) -> String:
match shape_id:
TOOL_SHAPE.SPHERE:
return "sphere"
TOOL_SHAPE.CYLINDER:
return "cylinder"
TOOL_SHAPE.CYLINDER_INF_H:
return "cylinder_inf_h"
TOOL_SHAPE.BOX:
return "box"
TOOL_SHAPE.BOX_INF_H:
return "box_inf_h"
return ""
func _init_default_project_settings() -> void:
for entry in _custom_settings:
if not ProjectSettings.has_setting(entry["name"]):
ProjectSettings.set(entry["name"], entry["default"])
ProjectSettings.set_initial_value(entry["name"], entry["default"])
ProjectSettings.add_property_info(entry)
if entry.has("basic") and ProjectSettings.has_method(&"set_as_basic"):
ProjectSettings.call(&"set_as_basic", entry["name"], entry["basic"])
func _update_gui() -> void:
if _grass_selected != null:
if not _grass_selected.sgt_tool_shape.has("airbrush"):
_grass_selected.sgt_tool_shape["airbrush"] = TOOL_SHAPE.CYLINDER
if not _grass_selected.sgt_tool_shape.has("pencil"):
_grass_selected.sgt_tool_shape["pencil"] = TOOL_SHAPE.CYLINDER
if not _grass_selected.sgt_tool_shape.has("eraser"):
_grass_selected.sgt_tool_shape["eraser"] = TOOL_SHAPE.SPHERE
_gui_toolbar.slider_radius.value = _grass_selected.sgt_radius
_gui_toolbar.slider_density.value = _grass_selected.sgt_density
_gui_toolbar.edit_scale.value = _grass_selected.sgt_scale
_gui_toolbar.edit_rotation.value = _grass_selected.sgt_rotation
_gui_toolbar.edit_rotation_rand.value = _grass_selected.sgt_rotation_rand
_gui_toolbar.edit_distance.value = _grass_selected.sgt_dist_min
_gui_toolbar.edit_slope_range.set_value(_grass_selected.sgt_slope.x, _grass_selected.sgt_slope.y)
_gui_toolbar.set_current_grass(_grass_selected)
_gui_toolbar_up.set_current_grass(_grass_selected)
if _grass_selected.multimesh != null:
_gui_toolbar.label_stats.text = "Count: " + str(_grass_selected.multimesh.instance_count)
_raycast_3d.collision_mask = _grass_selected.collision_mask
func _update_pointer() -> void:
if _grass_selected == null:
_pointer_decal.visible = false
return
_pointer_decal.visible = true
var tool_name := ""
match _edit_tool:
TOOL.NONE:
_pointer_decal.visible = false
return
TOOL.PENCIL:
tool_name = "pencil"
_pointer_rotate = true
TOOL.AIRBRUSH:
tool_name = "airbrush"
_pointer_rotate = true
TOOL.ERASER:
tool_name = "eraser"
_pointer_rotate = false
match _grass_selected.sgt_tool_shape[tool_name]:
TOOL_SHAPE.SPHERE:
if _edit_tool == TOOL.ERASER:
_pointer_rotate = true
_pointer_depth = _edit_radius
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_circle)
TOOL_SHAPE.CYLINDER:
if _edit_tool == TOOL.ERASER:
_pointer_rotate = true
_pointer_depth = DEFAULT_POINTER_DEPTH
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_circle)
TOOL_SHAPE.CYLINDER_INF_H:
_pointer_depth = 1000000
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_circle)
TOOL_SHAPE.BOX:
if _edit_tool == TOOL.ERASER:
_pointer_rotate = true
_pointer_depth = _edit_radius
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_rect)
TOOL_SHAPE.BOX_INF_H:
_pointer_depth = 1000000
_pointer_decal.set_texture(Decal.TEXTURE_ALBEDO, _pointer_img_rect)
func _on_project_settings_changed() -> void:
_prev_config = _custom_config_memorize()
_evaluate_draw_time = get_custom_setting("SimpleGrassTextured/General/evaluate_draw_time")
func _on_button_airbrush_toggled(pressed : bool) -> void:
if pressed:
_edit_tool = TOOL.AIRBRUSH
elif _edit_tool == TOOL.AIRBRUSH:
_edit_tool = TOOL.NONE
_update_pointer()
func _on_button_pencil_toggled(pressed : bool) -> void:
if pressed:
_edit_tool = TOOL.PENCIL
elif _edit_tool == TOOL.PENCIL:
_edit_tool = TOOL.NONE
_update_pointer()
func _on_button_eraser_toggled(pressed : bool) -> void:
if pressed:
_edit_tool = TOOL.ERASER
elif _edit_tool == TOOL.ERASER:
_edit_tool = TOOL.NONE
_update_pointer()
func _on_slider_radius_value_changed(value : float) -> void:
_edit_radius = value
_update_pointer()
_pointer_decal.extents = Vector3(_edit_radius, _pointer_depth, _edit_radius)
if _grass_selected != null:
_grass_selected.sgt_radius = value
func _on_slider_density_value_changed(value : float) -> void:
_edit_density = value
if _grass_selected != null:
_grass_selected.sgt_density = value
func _on_edit_slope_range_changed(value_min: float, value_max: float) -> void:
_edit_slope = Vector2(value_min, value_max)
if _grass_selected != null:
_grass_selected.sgt_slope = _edit_slope
func _on_edit_scale_value_changed(value : float) -> void:
_edit_scale = Vector3(value, value, value)
if _grass_selected != null:
_grass_selected.sgt_scale = value
func _on_edit_rotation_value_changed(value : float) -> void:
_edit_rotation = value
if _grass_selected != null:
_grass_selected.sgt_rotation = value
func _on_edit_rotation_rand_value_changed(value : float) -> void:
_edit_rotation_rand = value
if _grass_selected != null:
_grass_selected.sgt_rotation_rand = value
func _on_edit_distance_value_changed(value : float) -> void:
if _grass_selected != null:
_grass_selected.sgt_dist_min = value
func _on_set_tool(value : TOOL) -> void:
_edit_tool = value
if _edit_tool == TOOL.AIRBRUSH:
_pointer_decal.modulate = Color.WHITE
_gui_toolbar.slider_density.editable = true
_gui_toolbar.button_density.disabled = false
_gui_toolbar.button_airbrush.button_pressed = true
_gui_toolbar.button_pencil.button_pressed = false
_gui_toolbar.button_eraser.button_pressed = false
_gui_toolbar.set_density_modulate(Color.WHITE)
elif _edit_tool == TOOL.PENCIL:
_pointer_decal.modulate = Color.YELLOW
_gui_toolbar.slider_density.editable = false
_gui_toolbar.button_density.disabled = true
_gui_toolbar.button_airbrush.button_pressed = false
_gui_toolbar.button_pencil.button_pressed = true
_gui_toolbar.button_eraser.button_pressed = false
_gui_toolbar.set_density_modulate(Color(1, 1, 1, 0.25))
elif _edit_tool == TOOL.ERASER:
_pointer_decal.modulate = Color.RED
_gui_toolbar.slider_density.editable = false
_gui_toolbar.button_density.disabled = true
_gui_toolbar.button_airbrush.button_pressed = false
_gui_toolbar.button_pencil.button_pressed = false
_gui_toolbar.button_eraser.button_pressed = true
_gui_toolbar.set_density_modulate(Color(1, 1, 1, 0.25))
if _grass_selected != null:
_pointer_decal.visible = _edit_tool != TOOL.NONE
else:
_pointer_decal.visible = false
func _eval_brush() -> void:
if _grass_selected == null:
return
if _edit_tool == TOOL.PENCIL:
var steep : float = _grass_selected.sgt_dist_min
var list_trans := []
var follow_normal : bool = _grass_selected.sgt_follow_normal
var slope := Vector2(deg_to_rad(_grass_selected.sgt_slope.x), deg_to_rad(_grass_selected.sgt_slope.y))
if steep < 0.05:
steep = 0.4
_grass_selected.temp_dist_min = steep
var x := -_edit_radius
while x < _edit_radius:
var z := -_edit_radius
while z < _edit_radius:
var variation: Vector3
match _grass_selected.sgt_tool_shape["pencil"]:
TOOL_SHAPE.SPHERE, TOOL_SHAPE.CYLINDER, TOOL_SHAPE.CYLINDER_INF_H:
variation = Vector3(x + (randf() * steep * 0.5), 0, z + (randf() * steep * 0.5))
if variation.length() >= _edit_radius:
z += steep
continue
TOOL_SHAPE.BOX, TOOL_SHAPE.BOX_INF_H:
variation = Vector3(x + (randf() * steep * 0.5), 0, z + (randf() * steep * 0.5))
_:
variation = Vector3(x + (randf() * steep * 0.5), 0, z + (randf() * steep * 0.5))
if variation.length() >= _edit_radius:
z += steep
continue
variation = _pointer_decal.to_global(variation) - _pointer_decal.global_position
_raycast_3d.global_transform.basis.x = Vector3.RIGHT
_raycast_3d.global_transform.basis.y = _normal_draw * -1
_raycast_3d.global_transform.basis.z = Vector3.BACK
_raycast_3d.global_transform.origin = _position_draw + _normal_draw + variation
_raycast_3d.target_position = Vector3(0, _pointer_depth, 0)
_raycast_3d.collision_mask = _grass_selected.collision_mask
_raycast_3d.force_raycast_update()
var pos_grass : Vector3 = _raycast_3d.get_collision_point()
if _raycast_3d.is_colliding() and _raycast_3d.get_collider() == _object_draw:
var normal := _raycast_3d.get_collision_normal()
if normal.angle_to(Vector3.UP) < slope.x or normal.angle_to(Vector3.UP) > slope.y:
z += steep
continue
if not follow_normal:
normal = Vector3.UP
list_trans.append(_grass_selected.eval_grass_transform(
_raycast_3d.get_collision_point() - _grass_selected.global_position,
normal,
_edit_scale,
deg_to_rad(_edit_rotation) + (PI * (_edit_rotation_rand - (randf() * _edit_rotation_rand * 2.0)))
))
z += steep
x += steep
_grass_selected.add_grass_batch(list_trans)
elif _edit_tool == TOOL.AIRBRUSH:
var follow_normal : bool = _grass_selected.sgt_follow_normal
var slope := Vector2(deg_to_rad(_grass_selected.sgt_slope.x), deg_to_rad(_grass_selected.sgt_slope.y))
for i in _edit_density:
var variation: Vector3
match _grass_selected.sgt_tool_shape["airbrush"]:
TOOL_SHAPE.SPHERE, TOOL_SHAPE.CYLINDER, TOOL_SHAPE.CYLINDER_INF_H:
variation = Vector3.RIGHT * _edit_radius * randf()
variation = variation.rotated(Vector3.UP, randf() * TAU)
TOOL_SHAPE.BOX, TOOL_SHAPE.BOX_INF_H:
variation = Vector3(randf_range(-1, 1), 0, randf_range(-1, 1)) * _edit_radius
_:
variation = Vector3.RIGHT * _edit_radius * randf()
variation = variation.rotated(Vector3.UP, randf() * TAU)
variation = _pointer_decal.to_global(variation) - _pointer_decal.global_position
_raycast_3d.global_transform.basis.x = Vector3.RIGHT
_raycast_3d.global_transform.basis.y = _normal_draw * -1
_raycast_3d.global_transform.basis.z = Vector3.BACK
_raycast_3d.global_transform.origin = _position_draw + _normal_draw + variation
_raycast_3d.target_position = Vector3(0, _pointer_depth, 0)
_raycast_3d.collision_mask = _grass_selected.collision_mask
_raycast_3d.force_raycast_update()
if _raycast_3d.is_colliding() and _raycast_3d.get_collider() == _object_draw:
var normal := _raycast_3d.get_collision_normal()
if normal.angle_to(Vector3.UP) < slope.x or normal.angle_to(Vector3.UP) > slope.y:
continue
if not follow_normal:
normal = Vector3.UP
_grass_selected.add_grass(
_raycast_3d.get_collision_point() - _grass_selected.global_position,
normal,
_edit_scale,
deg_to_rad(_edit_rotation) + (PI * (_edit_rotation_rand - (randf() * _edit_rotation_rand * 2.0)))
)
elif _edit_tool == TOOL.ERASER:
match _grass_selected.sgt_tool_shape["eraser"]:
TOOL_SHAPE.SPHERE:
_grass_selected.erase(_position_draw - _grass_selected.global_position, _edit_radius)
TOOL_SHAPE.CYLINDER:
_grass_selected.erase_cylinder(_position_draw - _grass_selected.global_position, _edit_radius, _pointer_depth, _edit_radius, _pointer_decal.global_transform)
TOOL_SHAPE.CYLINDER_INF_H:
_grass_selected.erase_cylinder(_position_draw - _grass_selected.global_position, _edit_radius, 1000000, _edit_radius, _pointer_decal.global_transform)
TOOL_SHAPE.BOX:
_grass_selected.erase_box(_position_draw, Vector3(_edit_radius, _edit_radius, _edit_radius) * 2, _pointer_decal.global_transform)
TOOL_SHAPE.BOX_INF_H:
_grass_selected.erase_box(_position_draw - _grass_selected.global_position, Vector3(_edit_radius, 1000000, _edit_radius) * 2, _pointer_decal.global_transform)
if _grass_selected.multimesh != null:
_gui_toolbar.label_stats.text = "Count: " + str(_grass_selected.multimesh.instance_count)

View File

@ -0,0 +1 @@
uid://60a3gi7u2kf

View File

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 16c0-1 .446169-1.677492.997208-2.004739s1.552788-.302657 1.935486-.612948c.39166-.317558.238883-1.354794-.506722-1.360997-.602093-.005-2.280917.114349-2.994345 1.000498 0 0 .02877-.342307.728246-1.143787.25749-.295051.845071-.831245 1.335226-1.142006.315855-.200253.304568-.7940042.07635-1.1019159-.215366-.2906622-.757754-.5139818-1.267426-.097492-.875141.7151419-1.535631 1.3688649-1.96025 2.2237409 0 0 .555629-1.8148729 1.43465-3.1148791.585017-.8652009 1.447696-1.7613109 1.817344-2.0254611.476676-.3406332.395132-.8215602.257622-1.0775902-.137511-.25603-.649361-.8320229-1.469324-.2618772-.819964.5701457-1.8201 1.6816705-2.348572 2.7445314s-1.1018056 2.5619382-1.2122906 3.1644092c-.110485.60247.5333328-3.1752147.6357058-5.079275.102374-1.9040603-.0477389-3.4964517-.3531978-3.9387761-.2751828-.3984826-.8833696-.4032313-1.1565766-.2908387s-.5957703.4281338-.4622839 1.2053489c.091674.5337666.3731939 2.7779204-.06879 5.3078203 0 0-.2057111-2.091578-1.1059456-3.5329404-.6953339-1.1132976-1.4709299-1.766534-1.9207789-1.9933816-.449849-.2268475-.9125047-.0586449-1.1722357.2318279-.259732.2904727-.2725999.796324.0722066 1.0619276.7473817.5757065 1.8873348 2.3387974 2.0981248 3.1994408.21079.8606435.4931243 2.5858981.4097173 3.5376743 0 0-.7080016-1.2931297-1.6459356-2.2119993-.9386135-.9195354-1.5936048-1.4640717-2.3630513-1.1946814-.4980943.1743873-.7806591 1.1030419-.3368274 1.4021851.5723157.3857416 1.5820789 1.3039366 2.1658692 2.0464946.7549572.960276 1.2957891 1.962209 1.4475241 3.041457 0 0-.6087356-1.160801-1.5415066-1.565491s-1.3957658-.363075-1.8337441-.146313c-.3852071.190644-.4632405 1.066396-.2526335 1.28813.282265.297177.7544938.392878 1.3193052.541587.5303985.139651 1.2418516.900316 1.2418516 1.900316h7z" fill="#46be00"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bsmcpupaj02v8"
path="res://.godot/imported/sgt_icon.svg-b482cb8a790485e8c790170e86f09785.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/sgt_icon.svg"
dest_files=["res://.godot/imported/sgt_icon.svg-b482cb8a790485e8c790170e86f09785.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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
svg/scale=1.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1 @@
<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m12 16c0-1 .446169-1.677492.997208-2.004739s1.552788-.302657 1.935486-.612948c.39166-.317558.238883-1.354794-.506722-1.360997-.602093-.005-2.280917.114349-2.994345 1.000498 0 0 .02877-.342307.728246-1.143787.25749-.295051.845071-.831245 1.335226-1.142006.315855-.200253.304568-.7940042.07635-1.1019159-.215366-.2906622-.757754-.5139818-1.267426-.097492-.875141.7151419-1.535631 1.3688649-1.96025 2.2237409 0 0 .555629-1.8148729 1.43465-3.1148791.585017-.8652009 1.447696-1.7613109 1.817344-2.0254611.476676-.3406332.395132-.8215602.257622-1.0775902-.137511-.25603-.649361-.8320229-1.469324-.2618772-.819964.5701457-1.8201 1.6816705-2.348572 2.7445314s-1.1018056 2.5619382-1.2122906 3.1644092c-.110485.60247.5333328-3.1752147.6357058-5.079275.102374-1.9040603-.0477389-3.4964517-.3531978-3.9387761-.2751828-.3984826-.8833696-.4032313-1.1565766-.2908387s-.5957703.4281338-.4622839 1.2053489c.091674.5337666.3731939 2.7779204-.06879 5.3078203 0 0-.2057111-2.091578-1.1059456-3.5329404-.6953339-1.1132976-1.4709299-1.766534-1.9207789-1.9933816-.449849-.2268475-.9125047-.0586449-1.1722357.2318279-.259732.2904727-.2725999.796324.0722066 1.0619276.7473817.5757065 1.8873348 2.3387974 2.0981248 3.1994408.21079.8606435.4931243 2.5858981.4097173 3.5376743 0 0-.7080016-1.2931297-1.6459356-2.2119993-.9386135-.9195354-1.5936048-1.4640717-2.3630513-1.1946814-.4980943.1743873-.7806591 1.1030419-.3368274 1.4021851.5723157.3857416 1.5820789 1.3039366 2.1658692 2.0464946.7549572.960276 1.2957891 1.962209 1.4475241 3.041457 0 0-.6087356-1.160801-1.5415066-1.565491s-1.3957658-.363075-1.8337441-.146313c-.3852071.190644-.4632405 1.066396-.2526335 1.28813.282265.297177.7544938.392878 1.3193052.541587.5303985.139651 1.2418516.900316 1.2418516 1.900316h7z" fill="#46be00"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1,44 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cubr1a55brlfp"
path="res://.godot/imported/sgt_icon_48.svg-9d7978c1835cd43fb6d195cc3fe6772f.ctex"
metadata={
"has_editor_variant": true,
"vram_texture": false
}
[deps]
source_file="res://addons/simplegrasstextured/sgt_icon_48.svg"
dest_files=["res://.godot/imported/sgt_icon_48.svg-9d7978c1835cd43fb6d195cc3fe6772f.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
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
svg/scale=3.0
editor/scale_with_editor_scale=true
editor/convert_colors_with_editor_theme=false

View File

@ -0,0 +1,55 @@
# sgt_inspector.gd
# This file is part of: SimpleGrassTextured
# Copyright (c) 2023 IcterusGames
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
extends EditorInspectorPlugin
func _can_handle(object: Object) -> bool:
if object != null:
if object.has_meta("SimpleGrassTextured"):
return true
return false
func _parse_category(object: Object, category: String):
if category != "grass.gd":
return
var hbox = HBoxContainer.new()
var label := RichTextLabel.new()
var button := Button.new()
label.fit_content = true
label.bbcode_enabled = true
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
label.text = "[b]by IcterusGames:[/b]"
button.text = "About"
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
button.pressed.connect(_on_button_about_pressed.bind(button))
hbox.add_child(label)
hbox.add_child(button)
add_custom_control(hbox)
func _on_button_about_pressed(button :Button):
var win = load("res://addons/simplegrasstextured/gui/about.tscn").instantiate()
button.get_window().add_child(win)
win.popup_centered()

View File

@ -0,0 +1 @@
uid://cp54123dwlv7j

View File

@ -0,0 +1,40 @@
/* blur1.gdshader
This file is part of: SimpleGrassTextured
Copyright (c) 2023 IcterusGames
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
shader_type canvas_item;
uniform sampler2D normal_texture : hint_normal, filter_nearest, repeat_disable;
void fragment() {
vec2 pixel = 1.0 / vec2(textureSize(normal_texture, 0));
float lvl = round(float(textureSize(normal_texture, 0).y) * 0.01);
highp vec4 color = vec4(0.0);
highp float total = 0.0;
for(float x = -lvl * 2.0; x <= lvl * 2.0; x += 1.0) {
float gauss = (1.0 / (sqrt(TAU * lvl * lvl))) * pow(E, -((x * x) / (2.0 * lvl * lvl)));
color += texture(normal_texture, UV + vec2(0.0, x * pixel.x)) * gauss;
total += gauss;
}
COLOR = color / total;
}

View File

@ -0,0 +1 @@
uid://dii7jpdyaypc6

View File

@ -0,0 +1,42 @@
/* blur2.gdshader
This file is part of: SimpleGrassTextured
Copyright (c) 2023 IcterusGames
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
shader_type canvas_item;
uniform sampler2D normal_frame : hint_default_transparent, filter_nearest, repeat_disable;
uniform sampler2D normal_texture : hint_normal, filter_nearest, repeat_disable;
void fragment() {
vec2 pixel = 1.0 / vec2(textureSize(normal_texture, 0));
float lvl = round(float(textureSize(normal_texture, 0).y) * 0.01);
highp vec4 color = vec4(0.0);
highp float total = 0.0;
for(float x = -lvl * 2.0; x <= lvl * 2.0; x += 1.0) {
float gauss = (1.0 / (sqrt(TAU * lvl * lvl))) * pow(E, -((x * x) / (2.0 * lvl * lvl)));
color += texture(normal_texture, UV + vec2(x * pixel.x, 0.0)) * gauss;
total += gauss;
}
vec4 color_frame = texture(normal_frame, UV);
COLOR = ((color / total) * (1.0 - color_frame.a)) + color_frame;
}

View File

@ -0,0 +1 @@
uid://ct0c3akkhiaf

View File

@ -0,0 +1,47 @@
/* distance.gdshader
This file is part of: SimpleGrassTextured
Copyright (c) 2023 IcterusGames
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
shader_type spatial;
render_mode blend_mix,depth_draw_never,cull_back,unshaded,ambient_light_disabled;
uniform sampler2D heightmap_texture : hint_default_white, repeat_disable, filter_nearest;
uniform sampler2D depth_texture : hint_depth_texture, repeat_disable, filter_nearest;
global uniform int sgt_legacy_renderer = 0;
void fragment() {
vec2 uv = SCREEN_UV;
uv.y = 1.0 - uv.y;
float depth = textureLod(depth_texture, uv, 0.0).r;
// If using Compatibility renderer, apply proper depth range
vec3 ndc = mix(vec3(uv * 2.0 - 1.0, depth), vec3(uv, depth) * 2.0 - 1.0, float(sgt_legacy_renderer));
vec4 upos = INV_PROJECTION_MATRIX * vec4(ndc, 1.0);
float pos_y = -upos.z + CAMERA_POSITION_WORLD.y;
vec3 color_h = textureLod(heightmap_texture, UV, 0).rgb;
highp float map_y = (((round(color_h.r * 255.0) - 75.0) * 180.0) + (round(color_h.g * 255.0) - 75.0) + color_h.b) - 16200.0;
float dist = clamp(abs(map_y - pos_y) / 0.35, 0.0, 1.0) * 65535.0;
ALBEDO.r = floor(dist / 256.0) / 255.0;
ALBEDO.g = (dist - round(ALBEDO.r * 65280.0)) / 255.0;
ALBEDO.b = 0.0;
ALPHA = 1.0;
}

View File

@ -0,0 +1 @@
uid://h3cmhcmf6wwy

View File

@ -0,0 +1,27 @@
/* grass.gdshader
This file is part of: SimpleGrassTextured
Copyright (c) 2023 IcterusGames
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_disabled,specular_schlick_ggx,world_vertex_coords;
#define FILTER_TYPE filter_linear_mipmap
#include "grass.gdshaderinc"

View File

@ -0,0 +1 @@
uid://bi3o8elbtqoni

View File

@ -0,0 +1,169 @@
/* grass.gdshaderinc
This file is part of: SimpleGrassTextured
Copyright (c) 2023 IcterusGames
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
#ifndef FILTER_TYPE
#define FILTER_TYPE filter_linear_mipmap
#endif
uniform vec4 albedo : source_color = vec4(1.0);
uniform sampler2D texture_albedo : source_color,FILTER_TYPE,repeat_disable;
uniform sampler2D texture_normal : hint_roughness_normal,FILTER_TYPE,repeat_enable;
uniform sampler2D texture_metallic : hint_default_white,FILTER_TYPE,repeat_enable;
uniform sampler2D texture_roughness : hint_roughness_r,FILTER_TYPE,repeat_enable;
uniform vec2 texture_frames = vec2(1.0,1.0);
uniform int light_mode = 0;
uniform float alpha_scissor_threshold = 0.5;
uniform float normal_scale : hint_range(-16,16) = 1.0;
uniform float metallic : hint_range(0,1) = 0.0;
uniform vec4 metallic_texture_channel = vec4(1.0, 0.0, 0.0, 0.0);
uniform float roughness : hint_range(0,1) = 1.0;
uniform vec4 roughness_texture_channel = vec4(1.0, 0.0, 0.0, 0.0);
uniform float specular : hint_range(0,1) = 0.0;
uniform float scale_h = 1.0;
uniform float scale_w = 1.0;
uniform float scale_var : hint_range(-1.0, 1.0) = -0.25;
uniform float grass_strength : hint_range(0.0, 1.0) = 0.55;
uniform bool optimization_by_distance = false;
uniform float optimization_level = 7.0;
uniform float optimization_dist_min = 10.0;
uniform float optimization_dist_max = 50.0;
uniform float grass_size_y = 1.0;
uniform bool interactive_mode = true;
uniform float interactive_level_xz = 3.0;
uniform float interactive_level_y = 0.3;
global uniform vec3 sgt_player_position = vec3(100000.0);
global uniform vec3 sgt_player_mov = vec3(0.0);
global uniform sampler2D sgt_normal_displacement : repeat_disable, filter_nearest, hint_normal;
global uniform sampler2D sgt_motion_texture : repeat_disable, filter_nearest, hint_default_black;
global uniform vec3 sgt_wind_direction = vec3(1.0, 0.0, 0.0);
global uniform float sgt_wind_strength = 0.15;
global uniform float sgt_wind_turbulence = 1.0;
global uniform vec3 sgt_wind_movement = vec3(0.0, 0.0, 0.0);
global uniform sampler2D sgt_wind_pattern : source_color, repeat_enable, filter_linear, hint_default_white;
varying vec2 uv_align;
varying vec2 uv_scale;
varying flat vec3 v_normal_grass;
const highp float EPSILON = 0.0001;
float get_value(sampler2D tex, vec2 uv) {
vec4 color = textureLod(tex, uv, 0);
float value = round(color.r * 65280.0) + round(color.g * 255.0);
return value;
}
float wrap(float value, float min, float max) {
float range = max - min;
if (range < EPSILON) {
return min;
}
float result = value - (range * floor((value - min) / range));
if (result >= max - EPSILON && result <= max + EPSILON) {
return min;
}
return result;
}
void vertex() {
float lev = pow(abs((VERTEX.y - NODE_POSITION_WORLD.y) / grass_size_y), 1.7 + sgt_wind_strength);
float rand = wrap(dot(NODE_POSITION_WORLD, NODE_POSITION_WORLD * 10.0), 0.0, 256.0);
float randf = fract(rand);
int randi = int(randf * 10.0);
uv_align = vec2(float(randi % int(texture_frames.x)), float(int(randf * 100.0) % int(texture_frames.y)));
uv_align /= texture_frames;
uv_scale = vec2(1.0 / texture_frames.x, 1.0 / texture_frames.y);
float text_wind = texture(sgt_wind_pattern, (vec2(NODE_POSITION_WORLD.x, NODE_POSITION_WORLD.z) * 0.005) - sgt_wind_movement.xz).r;
VERTEX.x += text_wind * lev * sgt_wind_strength * sgt_wind_direction.x * (1.0 - grass_strength);
VERTEX.z += text_wind * lev * sgt_wind_strength * sgt_wind_direction.z * (1.0 - grass_strength);
VERTEX.x += sin(rand + sgt_wind_movement.y) * lev * ((1.0 - grass_strength) / 10.0);
VERTEX.x += sgt_wind_direction.x * lev * (sgt_wind_strength + (sin(TIME + rand) * 0.2 * (1.0 - grass_strength) * min(1.0, sgt_wind_turbulence))) * (1.0 - grass_strength);
VERTEX.y += sin(rand + sgt_wind_movement.y) * lev * ((1.0 - grass_strength) / 10.0) * sgt_wind_direction.y;
VERTEX.y += sgt_wind_direction.y * lev * (sgt_wind_strength + (sin(TIME + rand) * 0.2 * (1.0 - grass_strength) * min(1.0, sgt_wind_turbulence))) * (1.0 - grass_strength);
VERTEX.z += cos(rand + sgt_wind_movement.y) * lev * ((1.0 - grass_strength) / 10.0);
VERTEX.z += sgt_wind_direction.z * lev * (sgt_wind_strength + (sin(TIME + rand) * 0.2 * (1.0 - grass_strength) * min(1.0, sgt_wind_turbulence))) * (1.0 - grass_strength);
vec3 align = VERTEX - NODE_POSITION_WORLD;
float scale_rand = ((float(randi % 5) / 5.0) * scale_var);
float dist = 1.0;
if(optimization_by_distance) {
float node_dist = distance(NODE_POSITION_WORLD, MAIN_CAM_INV_VIEW_MATRIX[3].xyz);
dist = 1.0 - clamp(0.0, 1.0, float(randi % int(max(1.0, node_dist / optimization_level))));
dist *= smoothstep(optimization_dist_max, optimization_dist_min, node_dist);
}
vec3 scale = vec3(scale_w, scale_h, scale_w) * (1.0 + scale_rand);
VERTEX = NODE_POSITION_WORLD + (align * dist * scale);
if(dist > 0.0 && interactive_mode) {
float node_x = NODE_POSITION_WORLD.x / 50.0;
float node_z = NODE_POSITION_WORLD.z / 50.0;
vec2 uv_disp = vec2(clamp((node_x - (sgt_player_position.x - 0.5)), 0.0, 1.0), clamp((node_z - (sgt_player_position.z - 0.5)), 0.0, 1.0));
vec3 norm_mov = textureLod(sgt_normal_displacement, uv_disp - sgt_player_mov.xz, 0).rgb;
float time = get_value(sgt_motion_texture, uv_disp) / 65535.0;
norm_mov.xy = (norm_mov.xy * 2.0 - 1.0) * interactive_level_xz * lev;
VERTEX.xz += norm_mov.xy * (sin(time * time * 15.0) / 2.0) * (1.0 - time);
VERTEX.xz += norm_mov.xy * 0.5;
VERTEX.y -= (VERTEX.y - NODE_POSITION_WORLD.y) * norm_mov.b * interactive_level_y;
}
v_normal_grass = (MODELVIEW_MATRIX * vec4(0.0, 1.0, 0.0, 1.0)).xyz - NODE_POSITION_VIEW;
}
void light() {
switch(light_mode) {
case 0: // Lambert
DIFFUSE_LIGHT += clamp(dot(NORMAL, LIGHT), 0.0, 1.0) * ATTENUATION * 0.3 * LIGHT_COLOR;
break;
case 1: // Normal grass
DIFFUSE_LIGHT += clamp(dot(v_normal_grass, LIGHT), 0.0, 1.0) * ATTENUATION * 0.3 * LIGHT_COLOR;
break;
case 2: // Unshaded
DIFFUSE_LIGHT = vec3(1.0);
break;
}
}
void fragment() {
vec2 uv = (UV * uv_scale) + uv_align;
vec4 albedo_tex = texture(texture_albedo, uv);
ALBEDO = albedo.rgb * albedo_tex.rgb;
NORMAL_MAP = texture(texture_normal, uv).rgb;
NORMAL_MAP_DEPTH = normal_scale;
METALLIC = metallic;
ROUGHNESS = roughness;
SPECULAR = specular;
float metallic_tex = dot(texture(texture_metallic, uv), metallic_texture_channel);
METALLIC = metallic_tex * metallic;
float roughness_tex = dot(texture(texture_roughness, uv), roughness_texture_channel);
ROUGHNESS = roughness_tex * roughness;
ALPHA *= albedo.a * albedo_tex.a;
ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;
}

View File

@ -0,0 +1 @@
uid://cvdyxro7g1dic

View File

@ -0,0 +1,27 @@
/* grass_linear.gdshader
This file is part of: SimpleGrassTextured
Copyright (c) 2023 IcterusGames
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_disabled,specular_schlick_ggx,world_vertex_coords;
#define FILTER_TYPE filter_linear
#include "grass.gdshaderinc"

View File

@ -0,0 +1 @@
uid://bdx4su8bw3dmw

View File

@ -0,0 +1,27 @@
/* grass_nearest.gdshader
This file is part of: SimpleGrassTextured
Copyright (c) 2023 IcterusGames
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.*/
shader_type spatial;
render_mode blend_mix,depth_draw_opaque,cull_disabled,specular_schlick_ggx,world_vertex_coords;
#define FILTER_TYPE filter_nearest
#include "grass.gdshaderinc"

Some files were not shown because too many files have changed in this diff Show More