1a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk/* 2a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * Copyright (C) 2011 The Android Open Source Project 3a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * 4a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * Licensed under the Apache License, Version 2.0 (the "License"); 5a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * you may not use this file except in compliance with the License. 6a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * You may obtain a copy of the License at 7a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * 8a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * http://www.apache.org/licenses/LICENSE-2.0 9a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * 10a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * Unless required by applicable law or agreed to in writing, software 11a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * distributed under the License is distributed on an "AS IS" BASIS, 12a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * See the License for the specific language governing permissions and 14a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * limitations under the License. 15a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk */ 16a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 17a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukpackage com.android.scenegraph; 18a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 19a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport java.lang.Math; 20a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport java.util.ArrayList; 21a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 22e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchoukimport com.android.scenegraph.SceneManager; 23e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk 24a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport android.renderscript.*; 25a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport android.renderscript.Float3; 26a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport android.renderscript.Matrix4f; 27a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukimport android.util.Log; 28a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 29a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk/** 30a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk * @hide 31a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk */ 32a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchoukpublic class CompoundTransform extends Transform { 33a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 344bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public static abstract class Component { 35a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk String mName; 364bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk CompoundTransform mParent; 37e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk int mParentIndex; 38e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk protected ScriptField_TransformComponent_s.Item mData; 39e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk 40e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk Component(int type, String name) { 41e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData = new ScriptField_TransformComponent_s.Item(); 42e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk mData.type = type; 43e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mName = name; 44e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk } 45a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 46e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk void setNameAlloc() { 47e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk RenderScriptGL rs = SceneManager.getRS(); 48e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk if (mData.name != null) { 49e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk return; 50e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk } 51e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.name = SceneManager.getCachedAlloc(getName()); 52e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk if (mData.name == null) { 53e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.name = SceneManager.getStringAsAllocation(rs, getName()); 54e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk SceneManager.cacheAlloc(getName(), mData.name); 55e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk } 56e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk } 57e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk 58e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk ScriptField_TransformComponent_s.Item getRSData() { 59e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk setNameAlloc(); 60e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk return mData; 61e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk } 62e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk 63e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk protected void update() { 64e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk if (mParent != null) { 65e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mParent.updateRSComponent(this); 66e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk } 67e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk } 68e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk 69a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public String getName() { 70a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk return mName; 71a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 72a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 73a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 74a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public static class TranslateComponent extends Component { 75a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public TranslateComponent(String name, Float3 translate) { 76e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk super(ScriptC_export.const_Transform_TRANSLATE, name); 77e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk setValue(translate); 78a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 794bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public Float3 getValue() { 80e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk return new Float3(mData.value.x, mData.value.y, mData.value.z); 814bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 824bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public void setValue(Float3 val) { 83e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.x = val.x; 84e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.y = val.y; 85e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.z = val.z; 86e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk update(); 874bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 88a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 89a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 90a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public static class RotateComponent extends Component { 91a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public RotateComponent(String name, Float3 axis, float angle) { 92e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk super(ScriptC_export.const_Transform_ROTATE, name); 93e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk setAxis(axis); 94e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk setAngle(angle); 95a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 964bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public Float3 getAxis() { 97e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk return new Float3(mData.value.x, mData.value.y, mData.value.z); 984bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 994bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public float getAngle() { 100e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk return mData.value.w; 1014bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 1024bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public void setAxis(Float3 val) { 103e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.x = val.x; 104e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.y = val.y; 105e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.z = val.z; 106e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk update(); 1074bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 1084bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public void setAngle(float val) { 109e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.w = val; 110e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk update(); 1114bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 112a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 113a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 114a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public static class ScaleComponent extends Component { 115a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public ScaleComponent(String name, Float3 scale) { 116e31264694e9729db49acbb2d32eab2703efc8501Alex Sakhartchouk super(ScriptC_export.const_Transform_SCALE, name); 117e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk setValue(scale); 118a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 1194bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public Float3 getValue() { 120e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk return new Float3(mData.value.x, mData.value.y, mData.value.z); 1214bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 1224bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk public void setValue(Float3 val) { 123e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.x = val.x; 124e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.y = val.y; 125e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mData.value.z = val.z; 126e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk update(); 1274bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 128a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 129a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 130e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk ScriptField_TransformComponent_s mComponentField; 131a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public ArrayList<Component> mTransformComponents; 132a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 133a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public CompoundTransform() { 134a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk mTransformComponents = new ArrayList<Component>(); 135a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 136a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 13757fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk public TranslateComponent addTranslate(String name, Float3 translate) { 13857fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk TranslateComponent c = new TranslateComponent(name, translate); 13957fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk addComponent(c); 14057fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk return c; 14157fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk } 14257fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk 14357fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk public RotateComponent addRotate(String name, Float3 axis, float angle) { 14457fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk RotateComponent c = new RotateComponent(name, axis, angle); 14557fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk addComponent(c); 14657fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk return c; 14757fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk } 14857fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk 14957fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk public ScaleComponent addScale(String name, Float3 scale) { 15057fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk ScaleComponent c = new ScaleComponent(name, scale); 15157fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk addComponent(c); 15257fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk return c; 15357fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk } 15457fc7109e4e1ec0a32d1661779e8d7babab0279bAlex Sakhartchouk 155a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public void addComponent(Component c) { 1564bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk if (c.mParent != null) { 1574bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk throw new IllegalArgumentException("Transform components may not be shared"); 1584bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 1594bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk c.mParent = this; 160e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk c.mParentIndex = mTransformComponents.size(); 161a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk mTransformComponents.add(c); 162e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk updateRSComponentAllocation(); 163a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 164a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 165a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk public void setComponent(int index, Component c) { 1664bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk if (c.mParent != null) { 1674bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk throw new IllegalArgumentException("Transform components may not be shared"); 1684bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 169e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk if (index >= mTransformComponents.size()) { 170e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk throw new IllegalArgumentException("Invalid component index"); 171e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk } 1724bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk c.mParent = this; 173e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk c.mParentIndex = index; 174a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk mTransformComponents.set(index, c); 175e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk updateRSComponent(c); 176a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 177a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 178e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk void updateRSComponent(Component c) { 179e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk if (mField == null || mComponentField == null) { 1804bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk return; 1814bda82de0bca754f3ce387e9968170c5122241a9Alex Sakhartchouk } 182e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mComponentField.set(c.getRSData(), c.mParentIndex, true); 183e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mField.set_isDirty(0, 1, true); 184e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk } 185e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk 186e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk void updateRSComponentAllocation() { 187e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk if (mField == null) { 188e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk return; 189e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk } 190e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk initLocalData(); 191e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk 192e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mField.set_components(0, mTransformData.components, false); 193e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mField.set_isDirty(0, 1, true); 194e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk } 195e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk 196e93db17a143ff560f92d94b68ce1e76f79db7902Alex Sakhartchouk void initLocalData() { 197e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk RenderScriptGL rs = SceneManager.getRS(); 198e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk int numComponenets = mTransformComponents.size(); 199e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk if (numComponenets > 0) { 200e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mComponentField = new ScriptField_TransformComponent_s(rs, numComponenets); 201e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk for (int i = 0; i < numComponenets; i ++) { 202e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk Component ith = mTransformComponents.get(i); 203e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mComponentField.set(ith.getRSData(), i, false); 204e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk } 205e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mComponentField.copyAll(); 206e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk 207e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk mTransformData.components = mComponentField.getAllocation(); 208e92c78ccd095ae1cb47198afb664f98571ce2148Alex Sakhartchouk } 209a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk } 210a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk} 211a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 212a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 213a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 214a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 215a7a211b8a68a7d3f5ff4409aa286db07f96c0550Alex Sakhartchouk 216