Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 50 additions & 1 deletion Packages/luticalab.core/Languages/English.json
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,56 @@
"normal_map_settings": "Normal Map Settings",
"normal_strength": "Normal Strength",
"height_scale": "Height Scale",
"normal_map_info": "Converts height map (grayscale) to normal map for 3D surface details."
"normal_map_info": "Converts height map (grayscale) to normal map for 3D surface details.",

"plugin_browser_title": "TextureCocktail Plugin Browser",
"plugin_browser_desc": "All TextureCocktailContent subclasses found in loaded assemblies are listed here.",
"plugin_browser_howto": "To create a plugin: inherit from TextureCocktailContent and create a matching shader.",
"plugin_refresh": "Refresh",
"plugin_count": "Registered Plugins ({0})",
"plugin_class": "Class",
"plugin_assembly": "Assembly",
"plugin_author": "Author",
"plugin_version": "Version",
"plugin_description": "Description",

"texture_optimizer_title": "Texture Optimizer",
"texture_optimizer_scan_settings": "Scan Settings",
"texture_optimizer_folder": "Folder to Scan",
"texture_optimizer_platform": "Target Platform",
"texture_optimizer_max_size": "Max Allowed Size (px)",
"texture_optimizer_show_issues": "Show Only Textures With Issues",
"texture_optimizer_scan_btn": "Scan Textures",
"texture_optimizer_found": "Found {0} texture(s). {1} selected.",
"texture_optimizer_select_all": "Select All",
"texture_optimizer_deselect_all": "Deselect All",
"texture_optimizer_apply_fixes": "Apply Recommended Fixes to Selected",
"texture_optimizer_applied": "Applied fixes to {0} texture(s). Re-scan to verify.",
"texture_optimizer_issues": "Issues",
"texture_optimizer_ping": "Ping Asset",
"texture_optimizer_fix": "Fix This Texture",

"ollama_title": "Ollama Local AI Connector",
"ollama_desc": "Connects to a local Ollama server. Input: text prompt (+ optional image). Output: AI-generated text.",
"ollama_server": "Server Configuration",
"ollama_url": "Ollama URL",
"ollama_list_models": "List Models",
"ollama_model": "Model",
"ollama_model_manual": "Model (manual)",
"ollama_prompt_section": "Prompt",
"ollama_prompt_label": "Text Prompt:",
"ollama_attach_texture": "Attach Texture (vision models)",
"ollama_send": "Send Prompt",
"ollama_cancel": "Cancel",
"ollama_response_section": "Response",
"ollama_no_response": "(no response yet)",
"ollama_input_context": "Input Image Context:",
"ollama_response_image": "Response Image:",
"ollama_save_image": "Save Response Image...",
"ollama_clear": "Clear",
"ollama_ready": "Ready. Configure server URL and click 'List Models'.",
"ollama_waiting": "Waiting for Ollama response...",
"ollama_vision_help": "Requires a vision model (e.g. llava). The texture is converted to PNG and sent as base64."
}

}
51 changes: 50 additions & 1 deletion Packages/luticalab.core/Languages/Japanese.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,56 @@
"normal_map_settings": "ノーマルマップ設定",
"normal_strength": "ノーマル強度",
"height_scale": "高さスケール",
"normal_map_info": "ハイトマップ(グレースケール)を3Dサーフェスディテール用のノーマルマップに変換します。"
"normal_map_info": "ハイトマップ(グレースケール)を3Dサーフェスディテール用のノーマルマップに変換します。",

"plugin_browser_title": "TextureCocktail プラグインブラウザー",
"plugin_browser_desc": "ロードされたアセンブリで見つかったすべてのTextureCocktailContentサブクラスがここに表示されます。",
"plugin_browser_howto": "プラグイン作成方法: TextureCocktailContentを継承し、同じ名前のシェーダーを作成してください。",
"plugin_refresh": "更新",
"plugin_count": "登録プラグイン ({0})",
"plugin_class": "クラス",
"plugin_assembly": "アセンブリ",
"plugin_author": "作者",
"plugin_version": "バージョン",
"plugin_description": "説明",

"texture_optimizer_title": "テクスチャ最適化ツール",
"texture_optimizer_scan_settings": "スキャン設定",
"texture_optimizer_folder": "スキャンするフォルダー",
"texture_optimizer_platform": "対象プラットフォーム",
"texture_optimizer_max_size": "最大許容サイズ (px)",
"texture_optimizer_show_issues": "問題のあるテクスチャのみ表示",
"texture_optimizer_scan_btn": "テクスチャをスキャン",
"texture_optimizer_found": "テクスチャ {0} 個を検出。{1} 個選択中。",
"texture_optimizer_select_all": "すべて選択",
"texture_optimizer_deselect_all": "すべて選択解除",
"texture_optimizer_apply_fixes": "選択したテクスチャに推奨修正を適用",
"texture_optimizer_applied": "{0} 個のテクスチャに修正を適用しました。再スキャンして確認してください。",
"texture_optimizer_issues": "問題点",
"texture_optimizer_ping": "アセットを強調表示",
"texture_optimizer_fix": "このテクスチャを修正",

"ollama_title": "Ollama ローカルAI連携",
"ollama_desc": "ローカルOllamaサーバーに接続します。入力: テキストプロンプト(+オプション画像)。出力: AI生成テキスト。",
"ollama_server": "サーバー設定",
"ollama_url": "Ollama URL",
"ollama_list_models": "モデル一覧",
"ollama_model": "モデル",
"ollama_model_manual": "モデル (手動入力)",
"ollama_prompt_section": "プロンプト",
"ollama_prompt_label": "テキストプロンプト:",
"ollama_attach_texture": "テクスチャを添付 (ビジョンモデル用)",
"ollama_send": "プロンプトを送信",
"ollama_cancel": "キャンセル",
"ollama_response_section": "応答",
"ollama_no_response": "(まだ応答なし)",
"ollama_input_context": "入力画像コンテキスト:",
"ollama_response_image": "応答画像:",
"ollama_save_image": "応答画像を保存...",
"ollama_clear": "クリア",
"ollama_ready": "準備完了。サーバーURLを設定して「モデル一覧」をクリックしてください。",
"ollama_waiting": "Ollamaの応答を待っています...",
"ollama_vision_help": "ビジョンモデル(例: llava)が必要です。テクスチャはPNGに変換されbase64で送信されます。"
}

}
51 changes: 50 additions & 1 deletion Packages/luticalab.core/Languages/Korean.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,55 @@
"normal_map_settings": "노말 맵 설정",
"normal_strength": "노말 강도",
"height_scale": "높이 스케일",
"normal_map_info": "하이트 맵(그레이스케일)을 3D 표면 디테일용 노말 맵으로 변환합니다."
"normal_map_info": "하이트 맵(그레이스케일)을 3D 표면 디테일용 노말 맵으로 변환합니다.",

"plugin_browser_title": "텍스처 칵테일 플러그인 브라우저",
"plugin_browser_desc": "로드된 어셈블리에서 발견된 모든 TextureCocktailContent 서브클래스가 여기에 나열됩니다.",
"plugin_browser_howto": "플러그인 생성 방법: TextureCocktailContent를 상속하고 동일한 이름의 셰이더를 생성하세요.",
"plugin_refresh": "새로 고침",
"plugin_count": "등록된 플러그인 ({0})",
"plugin_class": "클래스",
"plugin_assembly": "어셈블리",
"plugin_author": "제작자",
"plugin_version": "버전",
"plugin_description": "설명",

"texture_optimizer_title": "텍스처 최적화 도구",
"texture_optimizer_scan_settings": "스캔 설정",
"texture_optimizer_folder": "스캔할 폴더",
"texture_optimizer_platform": "대상 플랫폼",
"texture_optimizer_max_size": "최대 허용 크기 (px)",
"texture_optimizer_show_issues": "문제가 있는 텍스처만 표시",
"texture_optimizer_scan_btn": "텍스처 스캔",
"texture_optimizer_found": "텍스처 {0}개 발견. {1}개 선택됨.",
"texture_optimizer_select_all": "모두 선택",
"texture_optimizer_deselect_all": "모두 해제",
"texture_optimizer_apply_fixes": "선택된 항목에 권장 수정 사항 적용",
"texture_optimizer_applied": "{0}개 텍스처에 수정 사항이 적용되었습니다. 재스캔하여 확인하세요.",
"texture_optimizer_issues": "문제점",
"texture_optimizer_ping": "에셋 강조 표시",
"texture_optimizer_fix": "이 텍스처 수정",

"ollama_title": "Ollama 로컬 AI 연동",
"ollama_desc": "로컬 Ollama 서버에 연결합니다. 입력: 텍스트 프롬프트 (+ 선택적 이미지). 출력: AI 생성 텍스트.",
"ollama_server": "서버 설정",
"ollama_url": "Ollama URL",
"ollama_list_models": "모델 목록",
"ollama_model": "모델",
"ollama_model_manual": "모델 (직접 입력)",
"ollama_prompt_section": "프롬프트",
"ollama_prompt_label": "텍스트 프롬프트:",
"ollama_attach_texture": "텍스처 첨부 (비전 모델용)",
"ollama_send": "프롬프트 전송",
"ollama_cancel": "취소",
"ollama_response_section": "응답",
"ollama_no_response": "(아직 응답 없음)",
"ollama_input_context": "입력 이미지 컨텍스트:",
"ollama_response_image": "응답 이미지:",
"ollama_save_image": "응답 이미지 저장...",
"ollama_clear": "지우기",
"ollama_ready": "준비됨. 서버 URL을 설정하고 '모델 목록'을 클릭하세요.",
"ollama_waiting": "Ollama 응답을 기다리는 중...",
"ollama_vision_help": "비전 모델(예: llava)이 필요합니다. 텍스처가 PNG로 변환되어 base64로 전송됩니다."
}
}
8 changes: 8 additions & 0 deletions Packages/luticalab.texturecocktail/Editor/AI.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

80 changes: 80 additions & 0 deletions Packages/luticalab.texturecocktail/Editor/AI/AiBackendBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

namespace LuticaLab.TextureCocktail
{
/// <summary>
/// Payload sent to an AI backend.
/// </summary>
public struct AiRequest
{
/// <summary>Text prompt to send.</summary>
public string Prompt;

/// <summary>Optional image attachment for vision models. May be <c>null</c>.</summary>
public Texture2D AttachedImage;
}

/// <summary>
/// Response received from an AI backend.
/// </summary>
public struct AiResponse
{
/// <summary>Whether the call succeeded.</summary>
public bool Success;

/// <summary>The text portion of the response.</summary>
public string Text;

/// <summary>Optional decoded image returned in the response. May be <c>null</c>.</summary>
public Texture2D Image;

/// <summary>Error message when <see cref="Success"/> is <c>false</c>.</summary>
public string Error;
}

/// <summary>
/// Abstract base class for local / remote AI backends.
///
/// Implement this to add a new AI provider. The <see cref="OllamaConnector"/>
/// discovers all concrete subclasses at runtime and presents them in a dropdown.
///
/// Implementations live in <c>Editor/AI/</c> — see <see cref="OllamaBackend"/> and
/// <see cref="OpenAiCompatibleBackend"/> for reference examples.
/// </summary>
public abstract class AiBackendBase
{
/// <summary>Human-readable name shown in the backend selector.</summary>
public abstract string DisplayName { get; }

/// <summary>Default server URL pre-filled in the UI.</summary>
public abstract string DefaultServerUrl { get; }

/// <summary>
/// Whether this backend can accept an image alongside the text prompt.
/// When <c>false</c> the image attachment UI is hidden for this backend.
/// </summary>
public abstract bool SupportsImageInput { get; }

/// <summary>
/// Returns the list of model names available on the server.
/// Throw on network failure so the caller can surface the error.
/// </summary>
public abstract Task<List<string>> FetchModelsAsync(
string serverUrl,
CancellationToken ct = default);

/// <summary>
/// Sends a prompt and returns the response.
/// The implementation must not throw — return <see cref="AiResponse.Success"/> = false
/// with a populated <see cref="AiResponse.Error"/> instead.
/// </summary>
public abstract Task<AiResponse> SendPromptAsync(
string serverUrl,
string model,
AiRequest request,
CancellationToken ct = default);
}
}
11 changes: 11 additions & 0 deletions Packages/luticalab.texturecocktail/Editor/AI/AiBackendBase.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 68 additions & 0 deletions Packages/luticalab.texturecocktail/Editor/AI/AiTextureUtils.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using UnityEngine;

namespace LuticaLab.TextureCocktail
{
/// <summary>
/// Shared texture encoding/decoding utilities used by all AI backend implementations.
/// </summary>
public static class AiTextureUtils
{
/// <summary>
/// Encodes a <see cref="Texture2D"/> to a base64 PNG string.
/// Handles non-readable textures by blitting through a temporary RenderTexture.
/// Returns <c>null</c> on failure.
/// </summary>
public static string TextureToBase64(Texture2D tex)
{
if (tex == null) return null;
try
{
byte[] pngBytes;
if (tex.isReadable)
{
pngBytes = tex.EncodeToPNG();
}
else
{
var rt = new RenderTexture(tex.width, tex.height, 0, RenderTextureFormat.ARGB32);
Graphics.Blit(tex, rt);
RenderTexture.active = rt;
var readable = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
readable.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
readable.Apply();
RenderTexture.active = null;
rt.Release();
pngBytes = readable.EncodeToPNG();
UnityEngine.Object.DestroyImmediate(readable);
}
return Convert.ToBase64String(pngBytes);
}
catch (Exception ex)
{
Debug.LogWarning($"[AiTextureUtils] Could not encode texture to base64: {ex.Message}");
return null;
}
}

/// <summary>
/// Decodes a base64-encoded image (PNG/JPEG) into a <see cref="Texture2D"/>.
/// Returns <c>null</c> on failure.
/// </summary>
public static Texture2D Base64ToTexture(string base64)
{
if (string.IsNullOrEmpty(base64)) return null;
try
{
byte[] bytes = Convert.FromBase64String(base64);
var tex = new Texture2D(2, 2);
tex.LoadImage(bytes);
return tex;
}
catch
{
return null;
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading