XR_ANDROID_light_estimation_cubemap

מחרוזת שם

XR_ANDROID_light_estimation_cubemap

סוג התוסף

הרחבת המכונה

מספר שלוחה רשום

722

Revision

1

סטטוס האישור

לא אושר

תלות בתוסף ובגרסה

XR_ANDROID_light_estimation

תאריך השינוי האחרון

2025-08-06

סטטוס כתובת ה-IP

לא ידוע על תלונות על הפרת זכויות יוצרים שקשורות לכתובות IP.

שותפים ביצירת התוכן

סלאר חאן, Google
סקוט צ'אנג, Google
ג'ארד פיינדר, Google
ספנסר קווין, Google
לבנה צ'ן, Google
ניחב ג'יין, Google
יורגן שטורם, Google

סקירה כללית

התוסף הזה מבוסס על התוסף הבסיסי XR_ANDROID_light_estimation. הוא מוסיף תמיכה בהערכות תאורה של cubemap, שמספקות הערכות מפורטות יותר לגבי התאורה בסביבה הפיזית.

הערה

המנגנון לקבלת נתוני הערכת האור זהה לזה של התוסף הבסיסי, אלא שבזמן יצירת ה-handle של הערכת האור צריך לשרשר את XrCubemapLightEstimatorCreateInfoANDROID אל XrLightEstimatorCreateInfoANDROID.

בדיקת יכולות המערכת

typedef struct XrSystemCubemapLightEstimationPropertiesANDROID {
    XrStructureType    type;
    void*              next;
    XrBool32           supportsCubemapLightEstimation;
} XrSystemCubemapLightEstimationPropertiesANDROID;

תיאורי חברים

  • type הוא XrStructureType של המבנה הזה.
  • next הוא NULL או מצביע למבנה הבא בשרשרת מבנים. לא מוגדרים מבנים כאלה ב-OpenXR או בתוסף הזה.
  • supportsCubemapLightEstimation הוא XrBool32 , שמציין אם המערכת הנוכחית תומכת בהערכת אור של cubemap.

אפליקציה יכולה לבדוק אם המערכת יכולה לתמוך בהערכת אור של cubemap על ידי הרחבת XrSystemProperties עם מבנה XrSystemCubemapLightEstimationPropertiesANDROID כשקוראים ל-xrGetSystemProperties .

אם זמן הריצה מחזיר XR_FALSE עבור supportsCubemapLightEstimation ו-XrCubemapLightEstimatorCreateInfoANDROID שורשר אל XrLightEstimatorCreateInfoANDROID , זמן הריצה חייב להחזיר XR_ERROR_FEATURE_UNSUPPORTED מ-xrCreateLightEstimatorANDROID .

שימוש תקף (משתמע)

קבלת רזולוציות נתמכות של cubemap

XrResult xrEnumerateCubemapLightingResolutionsANDROID(
    XrInstance                                  instance,
    XrSystemId                                  systemId,
    uint32_t                                    resolutionCapacityInput,
    uint32_t*                                   resolutionCountOutput,
    uint32_t*                                   resolutions);

תיאורים של פרמטרים

  • instance הוא XrInstance שנוצר קודם.
  • systemId הוא הערך XrSystemId שאוחזר קודם על ידי xrGetSystem, שעבורו רוצים לקבל את הרזולוציות הנתמכות של המפה הקובייתית.
  • resolutionCapacityInput הוא uint32_t שמציין את המספר המקסימלי של רכיבים שאפשר לאחסן במערך resolutions.
  • resolutionCountOutput הוא מצביע ל-uint32_t שמוגדר על ידי זמן הריצה ומציין את מספר הרכיבים שנכתבו למערך resolutions על ידי זמן הריצה.
  • resolutions הוא מערך של uint32_t שאוכלס על ידי זמן הריצה ברזולוציות הנתמכות של מיפוי קובייה.

הרזולוציה של ה-cubemap מציינת את הרוחב והגובה של כל פאה של ה-cubemap בפיקסלים. ניב של 2 קריאות האפליקציה יכולה לבחור להשתמש באחת מהרזולוציות הנתמכות ב-XrCubemapLightEstimatorCreateInfoANDROID :: cubemapResolution כשיוצרים את ה-handle של מעריך האור. האפליקציה חייבת להקצות את כמות הזיכרון המתאימה לחברי מאגר התמונות של XrCubemapLightingDataANDROID על סמך הרזולוציה שנבחרה ופורמט הצבע.

שימוש תקף (משתמע)

  • צריך להפעיל את התוסף XR_ANDROID_light_estimation_cubemap לפני הקריאה ל-xrEnumerateCubemapLightingResolutionsANDROID
  • instance חייב להיות ידית XrInstance תקינה
  • resolutionCountOutput חייב להיות מצביע לערך uint32_t
  • אם resolutionCapacityInput לא שווה ל-0 , resolutions חייב להיות מצביע למערך של resolutionCapacityInput ערכי uint32_t

קודי החזרה

הצלחה

  • XR_SUCCESS

Failure

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SIZE_INSUFFICIENT
  • XR_ERROR_SYSTEM_INVALID
  • XR_ERROR_VALIDATION_FAILURE

קבלת פורמטים נתמכים של צבעים במפת קוביות

הספירה XrCubemapLightingColorFormatANDROID מזהה בזמן הריצה את פורמט הצבע של תאורת cubemap שבו יש להשתמש.

typedef enum XrCubemapLightingColorFormatANDROID {
    XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R32G32B32_SFLOAT_ANDROID = 1,
    XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R32G32B32A32_SFLOAT_ANDROID = 2,
    XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R16G16B16A16_SFLOAT_ANDROID = 3,
    XR_CUBEMAP_LIGHTING_COLOR_FORMAT_MAX_ENUM_ANDROID = 0x7FFFFFFF
} XrCubemapLightingColorFormatANDROID;

המשמעויות של סוגי ה-enum הן:

תיאור ה-Enum

XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R32G32B32_SFLOAT_ANDROID

פורמט צבע עם 3 ערוצים, שבו כל ערוץ הוא ערך נקודה צפה של 32 ביט.

XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R32G32B32A32_SFLOAT_ANDROID

פורמט צבע עם 4 ערוצים, שכל אחד מהם הוא ערך נקודה צפה של 32 ביט.

XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R16G16B16A16_SFLOAT_ANDROID

פורמט צבע עם 4 ערוצים, שכל אחד מהם הוא ערך נקודה צפה של 16 ביט.

XrResult xrEnumerateCubemapLightingColorFormatsANDROID(
    XrInstance                                  instance,
    XrSystemId                                  systemId,
    uint32_t                                    colorFormatCapacityInput,
    uint32_t*                                   colorFormatCountOutput,
    XrCubemapLightingColorFormatANDROID*        colorFormats);

תיאורים של פרמטרים

  • instance הוא XrInstance שנוצר קודם.
  • systemId הוא הערך XrSystemId שאוחזר קודם על ידי xrGetSystem, שעבורו רוצים לקבל את הרזולוציות הנתמכות של המפה הקובייתית.
  • colorFormatCapacityInput הוא uint32_t שמציין את המספר המקסימלי של רכיבים שאפשר לאחסן במערך colorFormats.
  • colorFormatCountOutput הוא מצביע ל-uint32_t שמוגדר על ידי זמן הריצה ומציין את מספר הרכיבים שנכתבו למערך colorFormats על ידי זמן הריצה.
  • colorFormats הוא מערך של XrCubemapLightingColorFormatANDROID שאוכלס על ידי זמן הריצה עם פורמטים נתמכים של צבעי cubemap.

ניב של 2 קריאות האפליקציה יכולה לבחור להשתמש באחד מפורמטי הצבע הנתמכים ב-XrCubemapLightEstimatorCreateInfoANDROID :: colorFormat כשיוצרים את ה-handle של מעריך האור. האפליקציה חייבת להקצות את כמות הזיכרון המתאימה לחברי מאגר התמונות של XrCubemapLightingDataANDROID על סמך פורמט הצבע שנבחר.

שימוש תקף (משתמע)

  • יש להפעיל את התוסף XR_ANDROID_light_estimation_cubemap לפני הקריאה ל-xrEnumerateCubemapLightingColorFormatsANDROID
  • instance חייב להיות ידית XrInstance תקינה
  • colorFormatCountOutput חייב להיות מצביע לערך uint32_t
  • אם colorFormatCapacityInput לא שווה ל-0 , ‏ colorFormats חייב להיות מצביע למערך של colorFormatCapacityInput ערכים מסוג XrCubemapLightingColorFormatANDROID

קודי החזרה

הצלחה

  • XR_SUCCESS

Failure

  • XR_ERROR_FUNCTION_UNSUPPORTED
  • XR_ERROR_HANDLE_INVALID
  • XR_ERROR_INSTANCE_LOST
  • XR_ERROR_RUNTIME_FAILURE
  • XR_ERROR_SIZE_INSUFFICIENT
  • XR_ERROR_SYSTEM_INVALID
  • XR_ERROR_VALIDATION_FAILURE

יצירת נקודת אחיזה להערכת תאורה של מפת קוביות

typedef struct XrCubemapLightEstimatorCreateInfoANDROID {
    XrStructureType                        type;
    const void*                            next;
    uint32_t                               cubemapResolution;
    XrCubemapLightingColorFormatANDROID    colorFormat;
    XrBool32                               reproject;
} XrCubemapLightEstimatorCreateInfoANDROID;

תיאורי חברים

  • type הוא XrStructureType של המבנה הזה.
  • next הוא NULL או מצביע למבנה הבא בשרשרת מבנים.
  • cubemapResolution הוא uint32_t שמציין את הרזולוציה של התאורה במפת הקוביות שבה רוצים להשתמש.
  • colorFormat הוא XrCubemapLightingColorFormatANDROID שמציין את פורמט הצבע של נתוני התאורה של המפה הקוביתית שבה רוצים להשתמש.
  • reproject הוא XrBool32 שמציין אם צריך לבצע הקרנה מחדש של התאורה של מפת הקוביות אל מרחב הבסיס של האפליקציה.

המבנה XrCubemapLightEstimatorCreateInfoANDROID מתאר את המידע ליצירת נקודת אחיזה XrLightEstimatorANDROID שיכולה לספק הערכות של תאורה במיפוי קובייתי. חבר XrCubemapLightEstimatorCreateInfoANDROID :: cubemapResolution חייב להיות מוגדר לאחת מהרזולוציות שמוחזרות על ידי xrEnumerateCubemapLightingResolutionsANDROID . חבר XrCubemapLightEstimatorCreateInfoANDROID :: colorFormat חייב להיות מוגדר לאחד מפורמטי הצבע שמוחזרים על ידי xrEnumerateCubemapLightingColorFormatsANDROID . אם האפליקציה לא מגדירה את הרזולוציה לאחת מהרזולוציות הנתמכות או את פורמט הצבע לאחד מפורמטי הצבע הנתמכים, זמן הריצה חייב להחזיר XR_ERROR_FEATURE_UNSUPPORTED מ-xrCreateLightEstimatorANDROID .

שימוש תקף (משתמע)

אומדני אור של מפת קוביות

typedef struct XrCubemapLightingDataANDROID {
    XrStructureType                type;
    void*                          next;
    XrLightEstimateStateANDROID    state;
    uint32_t                       imageBufferSize;
    uint8_t*                       imageBufferRight;
    uint8_t*                       imageBufferLeft;
    uint8_t*                       imageBufferTop;
    uint8_t*                       imageBufferBottom;
    uint8_t*                       imageBufferFront;
    uint8_t*                       imageBufferBack;
    XrQuaternionf                  rotation;
    XrTime                         centerExposureTime;
} XrCubemapLightingDataANDROID;

תיאורי חברים

  • type הוא XrStructureType של המבנה הזה.
  • next הוא NULL או מצביע למבנה הבא בשרשרת מבנים. המבנים התקינים הם XrAmbientLightANDROID ,‏ XrSphericalHarmonicsANDROID ו-XrDirectionalLightANDROID .
  • state הוא XrLightEstimateStateANDROID שמייצג את מצב הערכת התאורה.
  • imageBufferSize הוא uint32_t שמציין את גודל הבאפר של כל תמונה של פאה במפת הקוביות.
  • imageBufferRight הוא מאגר uint8_t שמכיל את תמונת הפנים הימנית של מפת הקוביות.
  • imageBufferLeft הוא מאגר uint8_t שמכיל את תמונת הפנים השמאלית של המפה הקוביתית.
  • imageBufferTop הוא מאגר uint8_t שמכיל את תמונת הפנים העליונה של מפת הקובייה.
  • imageBufferBottom הוא מאגר uint8_t שמכיל את תמונת הפנים התחתונות של מפת הקוביות.
  • imageBufferFront הוא מאגר uint8_t שמכיל את תמונת הפנים הקדמית של מפת הקוביות.
  • imageBufferBack הוא מאגר uint8_t שמכיל את תמונת הפנים האחוריות של מפת הקוביות.
  • rotation הוא XrQuaternionf שמציין את הסיבוב של מפת הקוביות.
  • centerExposureTime הוא XrTime שמציין את השעה שבה צולמה מפת הקוביות.

אפשר לשרשר את המבנה הזה ל-XrLightEstimateANDROID . זמן הריצה חייב לאכלס את המבנה הזה ב-xrGetLightEstimateANDROID רק אם נעשה שימוש ב-XrCubemapLightEstimatorCreateInfoANDROID כדי ליצור את נקודת האחיזה של מעריך האור. האפליקציה חייבת להקצות את כמות הזיכרון המתאימה למאגרי התמונות, בהתאם לערכים שמוגדרים ב-XrCubemapLightEstimatorCreateInfoANDROID :: cubemapResolution וב-XrCubemapLightEstimatorCreateInfoANDROID :: colorFormat כשיוצרים את ה-handle של מעריך התאורה. באפליקציה חובה להגדיר את XrCubemapLightingDataANDROID :: imageBufferSize לקיבולת של כל מאגר תמונות של פנים בבייט. אם האפליקציה לא משתמשת בהערכת תאורה של cubemap או אם XrCubemapLightingDataANDROID :: imageBufferSize לא גדול מספיק כדי שהסביבה בזמן הריצה תאכלס את מאגרי התמונות, הסביבה בזמן הריצה חייבת להגדיר את XrCubemapLightingDataANDROID :: state ל-XR_LIGHT_ESTIMATE_STATE_INVALID_ANDROID .

אם האפליקציה מגדירה את XrCubemapLightEstimatorCreateInfoANDROID :: reproject ל-XR_TRUE כשיוצרים את נקודת האחיזה של מעריך האור, זמן הריצה חייב להגדיר את XrCubemapLightingDataANDROID :: rotation לסיבוב הזהות ולוודא שהמיפוי מחדש של המפה הקובייתית הפנימית המסובבת מתבצע על הפאות של מפת קוביות הזהות במרחב הבסיס של האפליקציה.

פריסת ה-cubemap של התאורה זהה לפריסת ה-cubemap של OpenGL, כמו שמוצג בתמונה הבאה

‫XR ANDROID light estimation cubemap layout

איור 24. פריסת Cubemap.

שימוש תקף (משתמע)

  • צריך להפעיל את התוסף XR_ANDROID_light_estimation_cubemap לפני שמשתמשים ב-XrCubemapLightingDataANDROID
  • type חייב להיות XR_TYPE_CUBEMAP_LIGHTING_DATA_ANDROID
  • next חייב להיות NULL או מצביע תקין למבנה הבא בשרשרת מבנים
  • state חייב להיות ערך תקין של XrLightEstimateStateANDROID
  • imageBufferRight חייב להיות מצביע למערך של imageBufferSize ערכים מסוג uint8_t
  • imageBufferLeft חייב להיות מצביע למערך של imageBufferSize ערכים מסוג uint8_t
  • imageBufferTop חייב להיות מצביע למערך של imageBufferSize ערכים מסוג uint8_t
  • imageBufferBottom חייב להיות מצביע למערך של imageBufferSize ערכים מסוג uint8_t
  • imageBufferFront חייב להיות מצביע למערך של imageBufferSize ערכים מסוג uint8_t
  • imageBufferBack חייב להיות מצביע למערך של imageBufferSize ערכים מסוג uint8_t
  • הפרמטר imageBufferSize חייב להיות גדול מ-0

קוד לדוגמה להערכת תאורה

בדוגמת הקוד הבאה אפשר לראות איך מקבלים את כל הכמויות האפשריות של הערכת התאורה בזמן הריצה

XrSession session;  // Created at app startup
XrInstance instance; // Created at app startup
XrSpace appSpace;   // Created previously.
XrSystemId systemId; // Retrieved previously by xrGetSystem
PFN_xrCreateLightEstimatorANDROID xrCreateLightEstimatorANDROID; // Created previously.
PFN_xrDestroyLightEstimatorANDROID xrDestroyLightEstimatorANDROID; // Created previously.
PFN_xrGetLightEstimateANDROID xrGetLightEstimateANDROID; // Created previously.
PFN_xrEnumerateCubemapLightingResolutionsANDROID xrEnumerateCubemapLightingResolutionsANDROID; // Created previously.
PFN_xrEnumerateCubemapLightingColorFormatsANDROID xrEnumerateCubemapLightingColorFormatsANDROID; // Created previously.

XrSystemCubemapLightEstimationPropertiesANDROID props = {
  .type = XR_TYPE_SYSTEM_CUBEMAP_LIGHT_ESTIMATION_PROPERTIES_ANDROID};
XrSystemProperties base = {.type = XR_TYPE_SYSTEM_PROPERTIES,
                           .next = &props};
CHK_XR(xrGetSystemProperties(instance, systemId, &base));
if (!props.supportsCubemapLightEstimation) {
   // Cubemap light estimation is not supported
}

uint32_t cubemapResolution = 0;
std::vector<uint32_t> supportedCubemapResolutions;
uint32_t resolutionCount;
CHK_XR(xrEnumerateCubemapLightingResolutionsANDROID(
  instance, systemId, 0, &resolutionCount, nullptr));
supportedCubemapResolutions.resize(resolutionCount);
if (resolutionCount == 0) {
  // No cubemap lighting supported
} else {
  CHK_XR(xrEnumerateCubemapLightingResolutionsANDROID(
    instance, systemId, 0, &resolutionCount, supportedCubemapResolutions.data()));
  cubemapResolution = supportedCubemapResolutions[0];
}

uint32_t pixelCount = cubemapResolution * cubemapResolution;

XrCubemapLightingColorFormatANDROID colorFormat;
std::vector<XrCubemapLightingColorFormatANDROID> supportedColorFormats;
uint32_t colorFormatCount;
CHK_XR(xrEnumerateCubemapLightingColorFormatsANDROID(
  instance, systemId, 0, &colorFormatCount, nullptr));
supportedColorFormats.resize(colorFormatCount);
if (colorFormatCount == 0) {
  // No supported color formats for cubemap lighting. Cannot use cubemap
  // light estimation.
} else {
  CHK_XR(xrEnumerateCubemapLightingColorFormatsANDROID(
    instance, systemId, 0, &colorFormatCount, supportedColorFormats.data()));
  colorFormat = supportedColorFormats[0];
}

uint32_t pixelSize = 0;
switch (colorFormat) {
  case XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R32G32B32_SFLOAT_ANDROID:
    pixelSize = 3 * sizeof(float);
    break;
  case XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R32G32B32A32_SFLOAT_ANDROID:
    pixelSize = 4 * sizeof(float);
    break;
  case XR_CUBEMAP_LIGHTING_COLOR_FORMAT_R16G16B16A16_SFLOAT_ANDROID:
    pixelSize = 4 * sizeof(uint16_t);
    break;
  default:
    // Should not happen since the color format was validated previously.
    break;
}

uint32_t perFaceImageBufferSize = pixelCount * pixelSize;

XrLightEstimatorANDROID estimator;
XrCubemapLightEstimatorCreateInfoANDROID cubemapCreateInfo = {
    .type = XR_TYPE_CUBEMAP_LIGHT_ESTIMATOR_CREATE_INFO_ANDROID,
    .cubemapResolution = cubemapResolution,
    .colorFormat = colorFormat,
    .reproject = XR_TRUE
};
XrLightEstimatorCreateInfoANDROID basicCreateInfo = {
    .type = XR_TYPE_LIGHT_ESTIMATOR_CREATE_INFO_ANDROID,
    .next = &cubemapCreateInfo};
CHK_XR(xrCreateLightEstimatorANDROID(session, &basicCreateInfo, &estimator));

std::vector<uint8_t> cubemapBuffer(perFaceImageBufferSize * 6); // 6 faces * perFaceImageBufferSize

// Every frame
XrTime updateTime;  // Time used for the current frame's simulation update.

XrLightEstimateGetInfoANDROID info = {
    .type = XR_TYPE_LIGHT_ESTIMATE_GET_INFO_ANDROID,
    .space = appSpace,
    .time = updateTime,
};

XrCubemapLightingDataANDROID cubemap = {
    .type = XR_TYPE_CUBEMAP_LIGHTING_DATA_ANDROID,
    .next = nullptr,
    .imageBufferSize = perFaceImageBufferSize,
    .imageBufferRight = cubemapBuffer.data() + 0 * perFaceImageBufferSize,
    .imageBufferLeft = cubemapBuffer.data() + 1 * perFaceImageBufferSize,
    .imageBufferTop = cubemapBuffer.data() + 2 * perFaceImageBufferSize,
    .imageBufferBottom = cubemapBuffer.data() + 3 * perFaceImageBufferSize,
    .imageBufferFront = cubemapBuffer.data() + 4 * perFaceImageBufferSize,
    .imageBufferBack = cubemapBuffer.data() + 5 * perFaceImageBufferSize,
};

XrDirectionalLightANDROID directionalLight = {
    .type = XR_TYPE_DIRECTIONAL_LIGHT_ANDROID,
    .next = &cubemap,
};

XrSphericalHarmonicsANDROID totalSh = {
    .type = XR_TYPE_SPHERICAL_HARMONICS_ANDROID,
    .next = &directionalLight,
    .kind = XR_SPHERICAL_HARMONICS_KIND_TOTAL_ANDROID,
};

XrSphericalHarmonicsANDROID ambientSh = {
    .type = XR_TYPE_SPHERICAL_HARMONICS_ANDROID,
    .next = &totalSh,
    .kind = XR_SPHERICAL_HARMONICS_KIND_AMBIENT_ANDROID,
};

XrAmbientLightANDROID ambientLight = {
    .type = XR_TYPE_AMBIENT_LIGHT_ANDROID,
    .next = &ambientSh,
};

XrLightEstimateANDROID estimate = {
    .type = XR_TYPE_LIGHT_ESTIMATE_ANDROID,
    .next = &ambientLight,
};

XrResult result = xrGetLightEstimateANDROID(estimator, &info, &estimate);
if (result == XR_SUCCESS &&
    estimate.state == XR_LIGHT_ESTIMATE_STATE_VALID_ANDROID) {
  // use cubemap, directionalLight, totalSh, ambientSh, and
  // ambientLight if each struct has a valid state field

  if (cubemap.state == XR_LIGHT_ESTIMATE_STATE_VALID_ANDROID) {
    // use cubemap
    if (cubemapCreateInfo.reproject == XR_TRUE) {
      XrQuaternionf identityQuaternion = {0.0f, 0.0f, 0.0f, 1.0f};
      assert(memcmp(&cubemap.rotation, &identityQuaternion, sizeof(XrQuaternionf)) == 0);
    }
  }
}

// When you want to disable light estimation
CHK_XR(xrDestroyLightEstimatorANDROID(estimator));

פקודות חדשות

מבנים חדשים

New Enums

New Enum Constants

  • XR_ANDROID_LIGHT_ESTIMATION_CUBEMAP_EXTENSION_NAME
  • XR_ANDROID_light_estimation_cubemap_SPEC_VERSION
  • הרחבה של XrStructureType :

    • XR_TYPE_CUBEMAP_LIGHTING_DATA_ANDROID
    • XR_TYPE_CUBEMAP_LIGHT_ESTIMATOR_CREATE_INFO_ANDROID
    • XR_TYPE_SYSTEM_CUBEMAP_LIGHT_ESTIMATION_PROPERTIES_ANDROID

בעיות

היסטוריית הגרסאות

  • גרסה 1, ‏ 2025-12-05 (סלאר חאן)

    • תיאור ראשוני של התוסף